ZendAMF, beyond the bootstrap. Part2

In the last post we set up the groundwork for our application. Now let’s start building our Zend MVC application and hook it up to a MySQL database.

Database

Use the SQL code below to create the F1 Drivers table we’ll be accessing later on.

CREATE TABLE `f1_drivers` (
`id` int(11) NOT NULL auto_increment,
`name` varchar(250) default NULL,
`team` varchar(250) default NULL,
`points` float default NULL,
PRIMARY KEY  (`id`)
) ENGINE=MyISAM AUTO_INCREMENT=8 DEFAULT CHARSET=latin1;
INSERT INTO `f1_drivers` (`id`,`name`,`team`,`points`)
VALUES
(1,'Jenson Button','Brawn-Mercedes',21),
(2,'Rubens Barrichello','Brawn-Mercedes',15),
(3,'Jarno Trulli','Toyota',8.5),
(4,'Timo Glock','Toyota',10),
(5,'Nick Heidfeld','BMW Sauber',4),
(6,'Mark Webber','RBR-Renault',9.5),
(7,'Sebastian Vettel','RBR-Renault',10);

Model

Create a new class in your model folder and name it F1Drivers.php, then add the following:

getAllWhere('');

		if( $result )
			return $result;
	}

	public function getAllWhere( $stmt )
	{
		$tmpArray = array();

		if( $stmt == '' || !isset( $stmt ) )
			$result = $this->fetchAll();
		else
			$result = $this->fetchAll( $stmt );

		foreach( $result as $row )
		{
			array_push( $tmpArray, $row );
		}
		return $tmpArray;
	}
}

Our F1Drivers Model extends the Zend_Db_Table_Abstract class, our gateway to the database. If you look back to our bootstrap file you’ll remember we set the Zend_Db_Table_Abtract class to default to the adapter set in our config file, in this case PDO_MYSQL. This removes the need to write any DB connection code in our Model. :D

(In order to take advantage of the class autoloading feature in the latest version of Zend we have to abide by the Zend folder and class naming conventions, hence our Model/F1Drivers.php class is named “Model_F1Drivers” and our Dto/F1Driver.php class is named Dto_F1DriverDTO).

The Zend_Db_Table_Abstract class presumes the table we wish to access is also named ‘Model_F1Drivers’, we do however have the power to override this by specifying the name of our table in the protected variable ‘$_name’.

  protected $_name = "f1_drivers";

We can also specify the class we would like the results mapped to, in this case we’ll map it to the ‘Dto_F1DriverDTO’ class we’ll create shortly:

 protected $_rowClass = 'Dto_F1DriverDTO';

I’ve included a method I like to use, ‘getAllWhere( $stmt )’, which executes a query passed to it from the controller. The Zend_Db_Table_Abstract provides a number of methods for us to interact with our database including ‘fetchAll()’, ‘fetchRow()’. The results are placed within an array and returned.

That’s it for our Model!

Controller

The controller for our application is also the gateway to our service that will be accessed by our Views (Flex/Flash). It’s here that we take care of our backend logic and accessing our Models before returning the appropriate data to the view.

getAllWhere('');
	}
}

Here we’ve provided a public method ‘getDrivers()’ that can be called by our Views. This method creates an instance of our F1Drivers Model and returns the result from getAllWhere() method in an Array.

Data Transfer Object

Let’s create the DTO’s we’ll be using to transfer our data across our MVC application. Starting with our server-side DTO, create a new class in your ‘includes/apps/Formulaone/Dto/’ folder named ‘F1DriverDTO’:

toArray() as $key => $value )
		{
			$this->$key = $value;
		}
	}
}

In the class above I’ve extended Zend_Db_Table_Row_Abstract which enables the results returned in our Model to be mapped to this DTO. For each row returned from our query a new “Dto_F1DriverDTO” is created, passing the row data into the constructor method which in turn is passed on to the parent class. We then iterate through the data and store its values in the public properties of the DTO. (The parent class provides a ‘toArray()’ method through which we can retrieve the stored row data within an associative array).

The $_explicitType property enables typed objects passed from Flex/Flash to be mapped to the appropriate PHP class. In this case we want it to match an AS3 class named “F1DriverDTO”.

Create a new Flash or Flex project and create the following ActionScript DTO in the appropriate package folder:
[YourProjectFolder]/classes/com/flexgubbins/formulaone/model/dto/F1DriverDTO.as

package com.flexgubbins.formulaone.model.dto
{
        [Bindable]
	[RemoteClass(alias="F1DriverDTO")]
	public class F1DriverDTO
	{
		public var id		: int;
		public var name		: String;
		public var team		: String;
		public var points	: Number;
	}
}

View: Flex

Here’s some example code if you want to hook Flex up to our Zend application. I’ve simply set up a datagrid and remote object pointing to our ZendAMF service. When the creationComplete event is fired we call the ‘getDrivers()’ method in our AMF service gateway/controller. We then have a onResult method to handle the results returned to Flex.



        

One thing to note is the private variable I’ve declared:

private var driverDTO:F1DriverDTO;

By creating a reference to the F1DriverDTO class we’re ensuring it is compiled into our SWF, allowing the returned results to be properly mapped.

View: Flash

Flash acts differently to Flex when it comes to Remoting so i thought it was important to include an example. Create a new AS3 Flash document and place a DataGrid component on the stage, name it ‘dg’. Next enter the following code on frame #1 of your timeline:

import com.flexgubbins.formulaone.model.dto.F1DriverDTO;
import flash.net.registerClassAlias;
import fl.data.DataProvider;

// REGISTER REMOTING CLASS ALIAS'S
registerClassAlias( "F1DriverDTO", F1DriverDTO );

// INIT THE RESULTS DATAGRID
dg.columns = ["id", "name", "team", "points"];
dg.rowCount = 10;

// INIT THE REMOTING SERVICE AND RETRIEVE THE USERS
var nc:NetConnection = new NetConnection();
nc.connect( 'http://localhost/formulaone/' );
nc.call( 'F1DriversService.getDrivers', new Responder( onNetConnResult, onNetConnFault ) );

/////////////////////////////////////////////////////////
// EVENT HANDLERS
/////////////////////////////////////////////////////////

function onNetConnResult( e:* ) : void
{
	dg.dataProvider = new DataProvider( e as Array );
}

function onNetConnFault( e:* ) : void
{
	trace( e );
}

In flash we have to use the NetConnection object to connect to our remoting service and specify the Responder call handlers – onResult / onFault to handle our results/faults. To ensure our results are properly mapped to the correct class in Flash we have to use the RegisterClassAlias to preserve the AMF object type passed from Zend:

registerClassAlias( "F1DriverDTO", F1DriverDTO );

We also need to define the columns in our DataGrid to match the properties of our DTO:

dg.columns = ["id", "name", "team", "points"];

Run your Flex/Flash projects and you should see your DataGrids populated by the drivers table data and an array of F1DriverDTO objects traced to your output window.

Download the source

3 Comments on "ZendAMF, beyond the bootstrap. Part2"

  1. djheru says:

    Thanks for the informative post. I was wondering if you could help me with a problem. I have some DTO’s with object properties that are arrays of typed objects. When I trace the PHP, the arrays consist of typed objects as expected, but when I look at the data in the result the DTO is typed properly, but the arrays consist of generic objects. Have you ever encountered this problem?

  2. martmcb says:

    Hey djhereu,
    Sounds like it can’t find the class to deserialise to.

    In Flex: ensure you’re including a reference to the classes you want typed in your code. e.g. simply adding:

    private var driverDTO:F1DriverDTO;
    private var carDTO:F1CarDTO;
    private var teamDTO:F1TeamDTO;

    And in Flash: register the class aliases:
    registerClassAlias( “F1DriverDTO”, F1DriverDTO );
    registerClassAlias( “F1CarDTO”, F1CarDTO );
    registerClassAlias( “F1TeamDTO”, F1TeamDTO );

    This will ensure these classes get compiled into your SWF.

    Failing that, make sure you’re defining the remote class to map to in your DTO:

    package com.flexgubbins.formulaone.model.dto
    {
    [Bindable]
    [RemoteClass(alias="F1DriverDTO")]
    public class F1DriverDTO
    {
    public var id : int;
    public var name : String;
    public var team : String;
    public var points : Number;
    }
    }

    hope that helps!
    Martin

  3. djheru says:

    Thanks for the response. I had the mappings set in Flex, but still no luck. I ended up adding the ArrayElementType metadata to the array and that finally got flex to recognize the array elements as the proper type.

    [ArrayElementType("AddressDTO")]
    public var addresses:Array;

Got something to say? Go for it!

Spam Protection by WP-SpamFree