0
votes

I am in the early stages of setting out the architecture for a new project. A data driven Flex UI, to sit within a web page, calling an Amfphp Service for MySQL data to populate elements within the Flex UI.

So many examples of how to approach this are out of date or poorly written so I am looking not only to completely understand the flow of data but to put in place clear, robust practice. Please review my approach and let me know if you feel I could do better.

Say I want to display a List of 'Subjects', let's follow the process I have so far.

Amfphp Service

Having structured and populated a MySQL database, named 'AtlasData', I have developed my initial Amfphp Service which, using the Amfphp Back Office Service Browser, appears to be returning an array of strongly typed 'VoSubject' objects. On my development Mac (MAMP installed) within my 'amfphp/Services/vo folder I have the following 'VoSubject.php' file:

<?php
/**
 * Created by IntelliJ IDEA.
 * User: Chris
 * Date: 04/10/2014
 * Time: 18:31
 */

class VoSubject {

    /* *
     * This Class models one row of the MySQL table. It has one field
     * for each row of the Table and a special extra field.
     * The extra field is $_explicitType, and its value is the fully qualified
     * ActionScript Value Object I intend to use in the Flex application to model the data.
     * If you don‚t configure this field correctly, then in the Flex app you
     * will not get your strongly typed ActionScript class, but a dynamic object.
     * */

    public $subjectId;
    public $subjectName;

    // Explicit ActionScript class
    var $_explicitType = "VoSubject";

}

My Amfphp Service currently looks like this (note I have removed some of the methods for brevity):

<?php

require_once ('vo/VoSubject.php');

include ('DbAccess.php');

class AtlasService {

    // This simple function can be used to test the service.
    public function helloWorld() {
        return "Hello World";
    }

    public function getAllSubjects() {

        // Connect to the database using PHP Data Objects (PDO).

        try {

            /*
             * The DbAccess class is a Singleton class.
             * Create an instance of this class to access it's methods.
             */
            $db = DbAccess::getInstance();

            // Create a PHP Data Object.
            $pdo = $db->getPDO();

        } catch (PDOException $e) {

            print "Connection Error!: " . $e->getMessage() . "<br/>";
            die();
        }

        // Retrieve all rows from the AtlasData database 'subjects' Table.
        try {

            $tsql =
                'SELECT s.`subjectId`, s.`subjectName`
                FROM Subjects s';

            $stmt = $pdo->prepare($tsql);
            $stmt->execute();

            // Fetch all of the data and place in variable '$results'.
            $results = $stmt->fetchAll(PDO::FETCH_CLASS, 'VoSubject');

        } catch (PDOException $e) {

            print "Error when fetching data: " . $e->getMessage() . "<br/>";
            die();
        }

        // Close the database connection.
        $stmt = null;
        $pdo = null;

        // Return the array.
        return $results;
    }
}

Using the Amfphp Back Office - Service Browser to call the 'getAllSubjects' function the following is returned: Amfphp Back Office - Service Browser Result

It would appear that using the code

$results = $stmt->fetchAll(PDO::FETCH_CLASS, 'VoSubject')

has set $results to be an Array of VoSubject objects.

Flex Application

So now I want my Flex application to call the Amfphp Service function 'getAllSubjects.

I am an advocate of View Model Presenter when developing Flex projects which I know will escalate in complexity. In this project's infancy I have created the following:

views - SubjectBar_View (MXML file) presenters - SubjectBar_Presenter (ActionScript class) model - Model (ActionScript class)

My SubjectBar_View displays a List of subjects:

<s:List id="subjectList" dataProvider="{presenter.subjects}">
        <s:layout>
            <s:HorizontalLayout/>
        </s:layout>
        <s:itemRenderer>
            <fx:Component>
                <s:ItemRenderer>
                    <s:HGroup paddingLeft="2">
                        <s:Label text="{data.subjectName}" width="125"/>
                    </s:HGroup>
                </s:ItemRenderer>
            </fx:Component>
        </s:itemRenderer>
    </s:List>

My SubjectBar_Presenter provides the data source for the List to bind to and this property is set by calling a method in the Model:

package presenters {

import flash.events.Event;

import models.Model;

import mx.collections.ArrayCollection;
import mx.events.FlexEvent;
import vo.VoSubject;

[Bindable]
public class SubjectBar_Presenter {

    private var _model:Model = Model.getInstance();
    private var _subjects:ArrayCollection;

    public function get subjects():ArrayCollection {
        return _subjects;
    }

    public function set subjects(value:ArrayCollection):void {
        _subjects = value;
    }

// Constructor.
    public function SubjectBar_Presenter() {
        // Add an eventListener to listen for property changes in the Model.
        _model.addEventListener("subjectsChanged", onSubjectsChanged);
    }

    private function onSubjectsChanged(event:Event):void {

        // Update the property.
        this.subjects = _model.subjects;
    }

    public function onCreationComplete(event:FlexEvent):void {

        // Get all Subjects from MySQL database.
        _model.getAllSubjects();

    }
}
}

My Model connects to the server and calls the Amfphp Service function 'getAllSubjects':

package models {

import flash.events.Event;
import flash.events.EventDispatcher;
import flash.net.NetConnection;
import flash.net.Responder;

import mx.collections.ArrayCollection;

// Custom Events.
[Event(name="subjectsChanged", type="flash.events.Event")]

public class Model extends EventDispatcher {

    // Event Names.
    public static const SUBJECTS_CHANGED:String = "subjectsChanged";

    private static var _model:Model;
    private var _subjects:ArrayCollection;
    private var _netConnectionObject:NetConnection;
    private var _responder:Responder = new Responder(handleAllSubjects, null);

    public function get subjects():ArrayCollection {
        return _subjects;
    }

    public function set subjects(value:ArrayCollection):void {
        _subjects = value;

        // Dispatch an event to allow the Detail view to update.
        dispatchEvent(new Event(SUBJECTS_CHANGED));
    }

    public function get netConnectionObject():NetConnection {
        return _netConnectionObject;
    }

    public function set netConnectionObject(value:NetConnection):void {
        _netConnectionObject = value;
    }

    // Constructor.
    public function Model(pvt:PrivateClass) {
        // Call the 'init' function to carry out any preparatory work.
        this.init();
    }

    // Singleton creator.
    public static function getInstance():Model {
        if (Model._model == null) {
            Model._model = new Model(new PrivateClass());
            //trace("Singleton instantiated");
        }
        else {
            //trace("Sorry--already have a Singleton instantiated")
        }
        return Model._model;
    }

    private function init():void {

        // Call any preparatory functions here.
        this.createNetConnection();
    }


    private function createNetConnection():void {

        netConnectionObject = new NetConnection();

        //netConnection.connect( [server name] / [project folder] /amfphp);
        netConnectionObject.connect("http://localhost/amfphp-2.2.1/amfphp/index.php");
    }

    private function handleAllSubjects(result:Object):void{
        // trace(result.toString());

        // The PHP method returns an Array NOT an ArrayCollection.
        this.subjects = new ArrayCollection(result as Array);
    }

    public function getAllSubjects():void {

        // Call the AtlasService.
        //netConnection.call([Service Name]/[function name]", [Responder], [parameters]);
        netConnectionObject.call("AtlasService/getAllSubjects", new Responder(handleAllSubjects, null));
    }

}
}

class PrivateClass {
    public function PrivateClass() {
        //trace("Private class is up");
    }
}

In my Flex project 'src' folder I have created a 'vo' folder and created the following 'VoSubject' class to define my Subject Value Object:

package vo {
// This is the ActionScript Value Object class.
// This must match the PHP Value Object class defined within the amfphp/Services/vo folder.

[RemoteClass(alias="VoSubject")]
[Bindable]
public class VoSubject {

    public var subjectId:int;
    public var subjectName:String;

    // Constructor.
    public function VoSubject() {
    }
}
}

It is the use of this VoSubject class on the Flex side which I am unsure of. Is the line [RemoteClass(alias="VoSubject")] pointing to the php class in my amfphp/Services/vo folder? If so where is this relative to. Should it read [RemoteClass(alias="vo/VoSubject")] because my VoSubject.php class is within a folder named 'vo' within my Services folder?

If I debug my application the List is displayed and populated with the subjectNames. This is great. However it would appear that my subjects data source is an ArrayCollection of objects containing the subjectId and subjectName but not an ArrayCollection of VoSubject objects.

Can someone please explain how I ensure that my 'subjects' data source in my 'subjectBar_Presenter' class is an ArrayCollection of strongly typed VoSubject objects. Additionally if you feel that I could improve my approach I am very willing to learn.

Thank you for getting to the end! I look forward to your thoughts.

Chris

1

1 Answers

1
votes

The var $_explicitType = "VoSubject"; should point to your actionscript class, in your case 'vo.VoSubject'. The [RemoteClass(alias="VoSubject")] should match with the explicit type. So in short, both reference the actionscript class.

At first glance your code seems to follow the MVC pattern, which is always good. But I must admit that I quickly skimmed through all the code to find the actual question.