Categories
Articles

Parsley MVC, RemoteObject, Zend AMF and MySQL Basic Flex Example

After conquering a basic Parsley Framework Flex example I wanted to create a minimal example of using the Flex Parsley framework, Zend AMF and MySQL. The Flex implementation goal is to mold a model view controller framework using Parsley and to communicate via Flex RemoteObject to Zend AMF. Eventually I applied what I learned to revamp DefineIt.com with Parsley and Zend AMF.

There are many approached to using Parsley for MVC. The Parsley documentation is very light on the subject. These also entail including using other frameworks like Caringorm. In this example I kept it simply by just using the Parsley messaging features and creating my own controller class.

I found other examples of Parsley that go further into its features. You might look at Christophe Coenraets’ example. This is referenced from the Parsley documentation . Christophe includes an update and add feature to the application. It also contains a dynamically loaded image class. It is a bit over featured and thus over involved for a pure beginner. It is void of substantive explanation leaving you to explore the Parsley documentation to try to understand why one approach was chosen over another in code. Worse code has no comments. This should be a minimum requirement for any referenced sample code. However there are some good Flex and Parsley techniques in there that can be helpful and one to bookmark for your learning process.

Learn More About Flex 4 From Adobe

Parsley 2.3 is used and the SWCs are included in the example Flex 4 project download I have here. I did appreciate these SWCs also being included in the downloads from the Parsley download page. Lets you get to learning faster.

You can build this with the free Flex SDK by using the code in the src folder and be sure to include a path to the Parsley and Spicelib library.

When you run this application, the trace log will show you the interactions between the model, controller, view and service.

Application Class – Parsley_Configuration.mxml
This is the main MXML file. Line 15 links the Parsley_Configuration.mxml file that configures Parsley framework.

Line 16 tells Parsley to include this mxml file and thus allows us to inject the ApplicationModel data.

I put all locale language into the model to simplify the example. In the effort the ApplicationModel class for application level data only contains the locale language title for the application you can see referenced in the Label on lines 26 and 39.

Line 40 contains the Panel to display the data grid.

<?xml version="1.0" encoding="utf-8"?>
<s:Application xmlns:fx="http://ns.adobe.com/mxml/2009" 
			   xmlns:s="library://ns.adobe.com/flex/spark" 
			   xmlns:hellozend="com.alh.parsley.example.hellozend.*"
			   xmlns:parsley="http://www.spicefactory.org/parsley"
			   xmlns:mx="library://ns.adobe.com/flex/mx"
			   xmlns:sf="http://www.spicefactory.org/parsley"
			   minWidth="955" minHeight="600" 
			   xmlns:views="com.alh.parsley.example.hellozend.views.*"
			   >
	<fx:Declarations>
		<!-- 
		Parsley configuration MXML file
		-->
		<parsley:ContextBuilder config="Parsley_Configuration" />
		<sf:Configure/>
	</fx:Declarations>
	<fx:Script>
		<!&#91;CDATA&#91;
			import com.alh.parsley.example.hellozend.model.ApplicationModel;
			/**
			 * Parsley injects the ApplicationModel here.
			 * */
			&#91;Inject&#93;
			&#91;Bindable&#93;
			public var model:ApplicationModel;
			
		&#93;&#93;>
	</fx:Script>
	<!-- 
		UI
	-->
	<s:layout>
		<s:VerticalLayout gap="10" 
						  horizontalAlign="center"
						  paddingLeft="12" paddingRight="12" paddingBottom="12" paddingTop="12"
						  />
	</s:layout>
	<s:Label text="{model.lang_AppTitle}" fontSize="20"/>
	<views:AllMembersGrid/>
</s:Application>

Parsley Configuration – Parsley_Configuration.mxml
This is the file Parsley reads for configuration.

I included the RemoteObject definitions here as well. I am following Christophe Coenraets’ example on this. It makes sense to me to have this as part of the configuration file. Remember to change the YOUR_GATEWAY_URL on line 13 to reflect your link to the Zend AMF or other RemoteObject service.

You may note there is no services xml or configuration file. The channel information is in the MXML instead. You can learn more on this by reading
ZEND AMF (Action Message Format) Minimalist Example Using RemoteObject and MySQL

On lines 27 – 30 I have four Actionscript classes handling the model, view and controller. They are listed here so we can use the [Inject] metatag for Parsley.

<?xml version="1.0" encoding="utf-8"?>
<Objects 
	xmlns:fx="http://ns.adobe.com/mxml/2009"
	xmlns="http://www.spicefactory.org/parsley"
	xmlns:s="library://ns.adobe.com/flex/spark" 
	xmlns:hellozend="com.alh.parsley.example.hellozend.*" 
	xmlns:model="com.alh.parsley.example.hellozend.model.*" 
	xmlns:services="com.alh.parsley.example.hellozend.services.*" 
	xmlns:controller="com.alh.parsley.example.hellozend.controller.*">
	<fx:Declarations>
		<s:ChannelSet id = "zend_amf_channel_set">
			<s:channels>
				<s:AMFChannel uri="YOUR_GATEWAY_URL"/>
			</s:channels>
		</s:ChannelSet>
		<!-- MembershipService RemoteObject -->
		<s:RemoteObject	id="membershipService_ro" 
						destination="zend-amf" 
						source="MembershipService" 
						channelSet="{zend_amf_channel_set}"
						showBusyCursor="true" 
						>
		</s:RemoteObject>
		<!--
			Parsley defined objects slated for injection where &#91;Inject&#93; metatag appears.
		-->
		<model:ApplicationModel/>
		<model:MembershipModel/>
		<services:MembershipServiceRemote/>
		<controller:MembershipController/>
</fx:Declarations>
</Objects>

Application Model Class – ApplicationModel.as
This class defines application level data and we only have a language variable to simplify the example for localization.

package com.alh.parsley.example.hellozend.model
{
	/**
	 * Model for application level data
	 * */
	public class ApplicationModel
	{
		/**
		 * UI language in model to simplify example
		 * 
		 * Language for Application
		 * */
		[Bindable]
		public var lang_AppTitle:String = "Minimalist Parsley Zend AMF Example Using RemoteObject";;
	}
}

Membership Model Class – MembershipModel.as
This class defines membership data. Again you can see the variables to simplify the example for localization.

The key data here is the ArrayCollection on line 13 for the member data.

package com.alh.parsley.example.hellozend.model
{
	import mx.collections.ArrayCollection;
	/**
	 * Model for membership
	 * */
	public class MembershipModel
	{
		/**
		 * List of members;
		 * */
		[Bindable]
		public var members:ArrayCollection;
		/**
		 * UI language in model to simplify example
		 * 
		 * Language for AllMembersGrid view
		 * */
		[Bindable]
		public var lang_AllMembersGridTitle:String = "Membership Listing";
		[Bindable]
		public var lang_MemberKey:String = "Member Key";
		[Bindable]
		public var lang_FirstName:String = "First Name";
		[Bindable]
		public var lang_LastName:String = "Last Name";
		[Bindable]
		public var lang_EmailAddress:String = "Email Address";
		[Bindable]
		public var lang_GetMembers:String = "Get Members";
		[Bindable]
		public var lang_ClearMembers:String = "Clear";
	}
}

IMembershipService Class – IMembershipService.as
Interface to define methods for membership service classes. We only have one in the example: MembershipServiceRemote.

package com.alh.parsley.example.hellozend.services
{
	import mx.rpc.AsyncToken;
	/**
	 * Interface to define MembershipService classes
	 * */
	public interface IMembershipService
	{
		function getAllMembers():AsyncToken;
	}
}

[ad name=”Google Adsense”]
MembershipServiceRemote Class – MembershipServiceRemote.as
This class deals with the server service. Only the controller communicates with this class by calling the get getAllMembers() method.

Line 46 is the interface back to the MVC or to the application however you want to look at it. Here a Parsley message is created for the MembershipGetAllMembersEvent.GET_ALL_MEMBERS event that you will see the MembershipController class handles.

You could have the controller pass its own result and fault handlers to the getAllMembers() method and eliminate the MembershipGetAllMembersEvent.GET_ALL_MEMBERS message.

As a further simplification, the faults are all routed to the catchAllServiceErrorHandler(…) method where a view component is inserted breaking a clean MVC. You could create a message for the fault handler to dispatch and have the controller to handle this.

package com.alh.parsley.example.hellozend.services
{
	import com.alh.parsley.example.hellozend.events.MembershipGetAllMembersEvent;
	import com.alh.parsley.example.hellozend.services.IMembershipService;
	import mx.controls.Alert;
	import mx.rpc.AsyncResponder;
	import mx.rpc.AsyncToken;
	import mx.rpc.events.FaultEvent;
	import mx.rpc.events.ResultEvent;
	import mx.rpc.remoting.RemoteObject;
	/**
	 * IMembershipService class to handle RemoteObject data communications with server.
	 * */
	public class MembershipServiceRemote implements IMembershipService
	{
		/**
		 * Specify the remote service for Parsley to inject.
		 * */
		[Inject(id="membershipService_ro")]
		public var service:RemoteObject;		
		/**
		 * Parsley creates an event dispatcher function
		 * */
		[MessageDispatcher]
		public var dispatcher:Function;

		/**
		 * Get all members from RemoteObject service.
		 * */
		public function getAllMembers():AsyncToken
		{
			trace ("SERVICE: MembershipServiceRemote.getAllMembers()");
			var token:AsyncToken = service.getAllMembers();
			token.addResponder(
				new AsyncResponder(getAllMembersResultsHandler, catchAllServiceErrorHandler));
			return token;
		}
		/**
		 * ResultEvent handler for service.getAllMembers()
		 * */
		protected function getAllMembersResultsHandler(event:ResultEvent, result:Object):void
		{
			trace ("SERVICE: MembershipServiceRemote.getAllMembersResultsHandler()");
			//event.result == Array.
			var members:Array = event.result as Array;
			dispatcher( new MembershipGetAllMembersEvent(MembershipGetAllMembersEvent.GET_ALL_MEMBERS, members));
		}
		/**
		 * Default handler for MembershipServiceRemote calls
		 * */
		protected function catchAllServiceErrorHandler(e:FaultEvent):void
		{
			Alert.show(e.toString());
		}
	}
}

MembershipController Class – MembershipController.as
This controller ties in the MembershipModel class and the MembershipServiceRemote class on line 18 and 24 respectively.

The controller listens for the MembershipEvent events on line 29. The Parsley [MessageHandler] tag makes this happen. These events are dispatched in the view.

On line 46 the controller listens for the MembershipGetAllMembersEvent dispatched from the MembershipServiceRemote class.

package com.alh.parsley.example.hellozend.controller
{
	import com.alh.parsley.example.hellozend.events.MembershipEvent;
	import com.alh.parsley.example.hellozend.events.MembershipGetAllMembersEvent;
	import com.alh.parsley.example.hellozend.model.MembershipModel;
	import com.alh.parsley.example.hellozend.services.MembershipServiceRemote;
	import mx.collections.ArrayCollection;
	/**
	 * Controller for Membership model and views.
	 * */
	public class MembershipController
	{
		/**
		 * Parsley injects the MembershipModel here.
		 * */
		[Inject]
		[Bindable]
		public var model:MembershipModel;
		/**
		 * Parsley injects the MembershipService here.
		 * */
		[Inject]
		[Bindable]
		public var service:MembershipServiceRemote;
		/**
		 * Parsley identified handler for MembershipEvent
		 * */
		[MessageHandler]
		public function membershipEventHandler( message:MembershipEvent ):void
		{
			trace ("CONTROLLER: MembershipController.membershipEventHandler(...) - message.type:" + message.type);
			switch (message.type )
			{
				case MembershipEvent.GET_ALL_MEMBERS:
					service.getAllMembers();
					break
				case MembershipEvent.CLEAR_ALL_MEMBERS:
					model.members.removeAll();
					break
			}
		}
		/**
		 * Parsley identified handler for MembershipGetAllMembersEvent
		 * */
		[MessageHandler]
		public function membershipGetAllMembersEventHandler( message:MembershipGetAllMembersEvent ):void
		{
			trace ("CONTROLLER: MembershipController.membershipGetAllMembersEventHandler(...)");
			model.members = new ArrayCollection(message.members);
		}
	}
}

[ad name=”Google Adsense”]
Membership Grid View – AllMembersGrid.mxml
This is a Panel containing a DataGrid and Buttons for the user interaction.

Lines 27 and 28 sets up the class to dispatch Parsley messages. Line 35 dispatches the MembershipEvent.CLEAR_ALL_MEMBERS event and line 43 dispatches the MembershipEvent.GET_ALL_MEMBERS event. The MembershipController class handles these messages to update the model and communicate with the service as needed.

This view is tied to the MembershipModel class via Parsley on line 23. The DataGrid binds to the model members variable.

Other bindings for the language localizations appear as well.

<?xml version="1.0" encoding="utf-8"?>
<s:Panel xmlns:fx="http://ns.adobe.com/mxml/2009" 
		 xmlns:s="library://ns.adobe.com/flex/spark" 
		 xmlns:mx="library://ns.adobe.com/flex/mx"
		 xmlns:sf="http://www.spicefactory.org/parsley"
		 title = "{model.lang_AllMembersGridTitle}"
		>
	<fx:Declarations>
		<!-- 
		Parsely will manage this component.
		-->
		<sf:Configure/>
	</fx:Declarations>
	<fx:Script>
		<!&#91;CDATA&#91;
			import com.alh.parsley.example.hellozend.events.MembershipEvent;
			import com.alh.parsley.example.hellozend.model.MembershipModel;
			/**
			 * Parsley injects the MembershipModel here.
			 * */
			&#91;Inject&#93;
			&#91;Bindable&#93;
			public var model:MembershipModel;
			/**
			 * Parsley creates an event dispatcher function
			 * */
			&#91;MessageDispatcher&#93;
			public var dispatcher:Function;
			/**
			 * Dispatch MembershipEvent.CLEAR_ALL_MEMBERS event
			 * */
			protected function clearDataGrid():void
			{
				trace("VIEW: " + className + ".clearDataGrid()");
				dispatcher( new MembershipEvent( MembershipEvent.CLEAR_ALL_MEMBERS ));
			}
			/**
			 * Dispatch MembershipEvent.GET_ALL_MEMBERS event
			 * */
			protected function getAllMembers():void
			{
				trace("VIEW: " + className + ".getAllMembers()");
				dispatcher( new MembershipEvent( MembershipEvent.GET_ALL_MEMBERS ));
			}
		&#93;&#93;>
	</fx:Script>
	<s:layout>
		<s:VerticalLayout gap="10" 
						  paddingLeft="12" paddingRight="12" paddingBottom="12" paddingTop="12"
						  />
	</s:layout>
	<mx:DataGrid  id="member_dg"  height="100" dataProvider="{model.members}">
		<mx:columns>
			<mx:DataGridColumn headerText="{model.lang_MemberKey}" dataField="memberKey"/>
			<mx:DataGridColumn headerText="{model.lang_FirstName}" dataField="firstName"/>
			<mx:DataGridColumn headerText="{model.lang_LastName}" dataField="lastName"/>
			<mx:DataGridColumn headerText="{model.lang_EmailAddress}" dataField="emailAddress" width="200"/>
		</mx:columns>
	</mx:DataGrid>
	<s:HGroup horizontalAlign="center" width="100%">
		<s:Button label="{model.lang_GetMembers}" click="{getAllMembers();}"/>
		<s:Button label="{model.lang_ClearMembers}" click="{clearDataGrid();}"/>
	</s:HGroup>

</s:Panel>

MembershipEvent.as
This is a standard Event class to inform the MVC framework all membership data is required or needs to be cleared. These free the view from being coupled with the service or updating the model.

package com.alh.parsley.example.hellozend.events
{
	import flash.events.Event;
	/**
	 * Events related to membership.
	 * */
	public class MembershipEvent extends Event
	{
		/**
		 * Request to retrieve all membership data
		 * */
		public static const GET_ALL_MEMBERS:String = "com.alh.parsley.example.hellozend.events.MembershipEvent.getAllMembers";
		/**
		 * Request to clear all membership data
		 * */
		public static const CLEAR_ALL_MEMBERS:String = "com.alh.parsley.example.hellozend.events.MembershipEvent.clearAllMembers";

		public function MembershipEvent(type:String, bubbles:Boolean=false, cancelable:Boolean=false)
		{
			super(type, bubbles, cancelable);
		}
	}
}

MembershipGetAllMembersEvent.as
This event message signals success retrieval of members data from the service.

package com.alh.parsley.example.hellozend.events
{
	import flash.events.Event;
	/**
	 * Event defining service receipt of membership data.
	 * */
	public class MembershipGetAllMembersEvent extends Event
	{
		public static const GET_ALL_MEMBERS:String = "com.alh.parsley.example.hellozend.events.MembershipGetAllMembersEvent.getAllMembers";
		/**
		 * Membership data
		 * */
		public var members:Array;
		
		public function MembershipGetAllMembersEvent(type:String, members:Array, bubbles:Boolean=false, cancelable:Boolean=false)
		{
			super(type, bubbles, cancelable);
			this.members = members;
		}
	}
}

Zend AMF Gateway PHP Script
This is the gateway program for the Zend Amf. This is the file that you reference on line 13 of the Parsley_Configuration.mxml file. In this example it was named index.php.

<?php
/**
*  Sample Zend AMF gateway
*  @return Endpoint &#91;Zend Amf Endpoint&#93;
* */

// Configurable values
// Debugging values
$debug = true;                             // Debugging status
if ($debug)
{
	// Report all errors, warnings, interoperability and compatibility
	error_reporting(E_ALL|E_STRICT);
	// Show errors with output
	ini_set("display_errors", "on");
}
else
{
	error_reporting(0);
	ini_set("display_errors", "off");
}
// Add the Zend AMF installation folder to the include path.
// In this example the frameworks folder is a sibling folder to
// this application folder. The frameworks folder contains the Zend
// folder that is extracted from http://framework.zend.com/download/amf
ini_set("include_path", ini_get("include_path") . PATH_SEPARATOR . "..\\frameworks" );

// Instantiate the Zend Amf server
require_once 'Zend/Amf/Server.php';
$server = new Zend_Amf_Server();

// Register your service classes
require_once 'MembershipService.php';
$server->setClass("MembershipService");

//Map ActionScript value objects to the PHP value objects.
$server->setClassMap("MemberData", "MemberData");

// Return the handle.
echo ($server->handle());

?>

[ad name=”Google Adsense”]
MembershipService Class
This is the service class that contains remote methods. Generally a the database and business logic is delegated to another API you write. In this case they are all together for simplicity.

<?php
/**
*	Service class exposing the methods to deal with membership.
*   This example includes business logic for simplicity of study.
*/
require_once 'MemberData.php';
class MembershipService
{
	public function MembershipService()
	{
		// Connect to MySql database.
		// Supply your own MySQL access values.
		// These are defaults when running on your own private computer.
		mysql_connect("localhost", "root", "");
		// Select the database.
		// Supply your own database name.
		mysql_select_db("test");
	}
	/**
	*	Get all members and all fields.
	*/
	public function getAllMembers()
	{
		// Array of MemberData objects.
		$members = array();
		// Selecting all fields and all records from table.
		// Supply your own table name and optionally your own SQL statement.
		$result = mysql_query("SELECT * FROM zend_amf_members");
		// Assuming mysql_query success. Slog through records.
		while ($row = mysql_fetch_assoc($result))
		{
			// Create a MemberData value object and populate.
			$member = new MemberData();
			$member->memberKey = $row["memberKey"];
			$member->firstName = $row["firstName"];
			$member->lastName = $row["lastName"];
			$member->emailAddress = $row["emailAddress"];
			array_push($members, $member);
		}
		// Return the members array to client.
		return $members;
	}
}
?>

MemberData Value Object Class for PHP
This is the value object on the server side to define the field names for a member object. This is mapped to the client side on line 37 of the Zend AMF gateway script.

<?php
/**
 * Value object defining the member data
 * */
class MemberData
{
  public $memberKey;	// uint
  public $firstName;	// String
  public $lastName;		// String
  public $emailAddress;	// String
}
?>

SQL To Create Testing Table
The PHP script uses zend_amf_members for the table and this is the SQL to create that table. In this example the database was called test.

SET SQL_MODE="NO_AUTO_VALUE_ON_ZERO";

--
-- Database: `test`
--

-- --------------------------------------------------------

--
-- Table structure for table `zend_amf_members`
--

CREATE TABLE IF NOT EXISTS `zend_amf_members` (
  `memberKey` int(10) unsigned NOT NULL auto_increment,
  `firstName` varchar(30) NOT NULL default '',
  `lastName` varchar(30) NOT NULL default '',
  `emailAddress` varchar(50) NOT NULL default '',
  PRIMARY KEY  (`memberKey`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1 COMMENT='Zend Amf Examples' AUTO_INCREMENT=7 ;

--
-- Dumping data for table `zend_amf_members`
--

INSERT INTO `zend_amf_members` (`memberKey`, `firstName`, `lastName`, `emailAddress`) VALUES
(1, 'Donald', 'Duck', 'quacker@pond.com'),
(2, 'Daffy', 'Duck', 'daft_2x@farm.org'),
(3, 'Elmer', 'Fudd', 'elmer.fudd@hunters.net'),
(4, 'Bugs', 'Bunny', 'whats_up_doc@underground.org'),
(5, 'Yosemite', 'Sam', 'varmint_chaser@forest.com'),
(6, 'Wile', 'Coyote', 'ceo@acme.com');

Categories
Articles

ZEND AMF (Action Message Format) Minimalist Example Using RemoteObject and MySQL

I always liked and appreciated the AMFPHP implementation of Adobe Action Message Format. I have seen it implemented in some robust applications and it held its own. Among its best features is the browser which is very handy for testing. However Adobe has now gotten involved with Zend AMF and this is a quick shot at putting it together using the RemoteObject class.

The first example I looked at is from Lee Brimelow’s Introduction to ZendAMF at gotoAndLearn.com. This is a great example and worth the watch to get a rounded view of using Zend AMF. He uses NetConnection and Responder classes in Flash CS4. You can use his example in Flex and Air.

Adobe Flash Builder 4 and Flex 4 - Essential Training
Learn More About Flex 4

Eventually I put Zend AMF to practical use by revamping DefineIt.com to use it along with the Parsley framework.

Flash CS4 does not have a native RemoteObject class, so that leads us to using Flex.

Download files
You can build this with the free Flex SDK by using the code in the src folder. This example was built with Flex 4.

The following uses the code from the Flex example, but other than the Application and WindowedApplication tags the code is the same.

Application Class – ZendAMFRemoteObjectGetTable_Flex
Rather than use a services-config.xml file linked to the compiler line, I choose to include the channel information in the MXML. More on this method is detailed by Chris Callendar’s post Using Flex and AMFPHP without a services-config.xml file.

<?xml version="1.0" encoding="utf-8"?>
<!--
Demonstrates use of the RemoteObject to communicate with Zend AMF.
<p>Author: Lon Hosford https://www.lonhosford.com 908 996 3773</p>
-->
<s:Application xmlns:fx="http://ns.adobe.com/mxml/2009"
			   xmlns:s="library://ns.adobe.com/flex/spark"
			   xmlns:mx="library://ns.adobe.com/flex/mx" minWidth="955" minHeight="600">
	<fx:Declarations>
		<!-- Alternative to services-config.xml added to the compiler option services -->
		<s:ChannelSet id = "zend_amf_channel_set">
			<s:channels>
				<s:AMFChannel uri="{GATEWAY_URL}"/>
			</s:channels>
		</s:ChannelSet>
		<!-- MembershipService RemoteObject -->
		<s:RemoteObject	id="members_ro"
						destination="zend-amf"
						source="MembershipService"
						channelSet="{zend_amf_channel_set}"
						showBusyCursor="true"
						fault="membersError(event)">
			<s:method name="getAllMembers" result="getAllMembersResult(event)"/>
		</s:RemoteObject>
	</fx:Declarations>

[ad name=”Google Adsense”]
On line 33 you need to set the GATEWAY_URL to match your configuration. You might want to use a default file name such as index.php for the gateway script so you only need a path to the folder.

	<fx:Script>
		<!&#91;CDATA&#91;
			import mx.controls.Alert;
			import mx.events.FlexEvent;
			import mx.rpc.events.FaultEvent;
			import mx.rpc.events.ResultEvent;
			// &#91;ADD YOUR HTTP URL TO THE GATEWAY PHP SCRIPT&#93;
			private const GATEWAY_URL:String = "http://YOUR_DOMAIN/PATH_TO_GATEWAY SCRIPT/";
			/**
			 * Member value object. Not used in this example. Included for information only.
			 * Use this to convert the data received. In this example the DataGrid dataprovider
			 * property converted the incoming amf array to an ArrayCollection and we mapped
			 * using the DataGridColumn dataField property.
			 * */
			private var memberData:MemberData;
			&#91;Bindable&#93;
			private var lang_title:String = "Minimalist Zend AMF Example Using RemoteObject";
			/**
			 * Invoke RemoteObject members_ro getAllMembers method.
			 * */
			protected function getAllMembers():void
			{
				members_ro.getAllMembers();
			}
			/**
			 * Empties the member_dg DataGrid.
			 * */
			protected function clearDataGrid():void
			{
				member_dg.dataProvider = {};
			}
			/**
			 * RemoteObject members_ro ResultEvent handler for the remote getAllMembers method.
			 * <p>Data arrives as an array and dataProvider property converts to ArrayCollection.
			 * The member_dg DataGrid contains DataGridColumn to match the expected field using
			 * the dataField property.</p>
			 * */
			protected function getAllMembersResult(e:ResultEvent):void
			{
				member_dg.dataProvider = e.result; // ResultEvent result property is an array
			}
			/**
			 * RemoteObject members_ro default FaultEvent handler.
			 * */
			protected function membersError(e:FaultEvent):void
			{
				Alert.show(e.toString());
			}
		&#93;&#93;>
	</fx:Script>

[ad name=”Google Adsense”]
This is the UI. The member_dg DataGrid contains DataGridColumn to match the expected fields in the MemberData object using the dataField property. This is a one to one relationship, however you may find times where you need to construct your own fields such as a concatenation of first and last name and thus populate the DataGrid with your own collection object.

	<s:VGroup horizontalAlign="center" width="100%" paddingTop="25">
		<s:Label text="{lang_title}" fontSize="20"/>

		<mx:DataGrid  id="member_dg"  height="100">
			<mx:columns>
				<mx:DataGridColumn headerText="Member Key" dataField="memberKey" width = "100"/>
				<mx:DataGridColumn headerText="First Name" dataField="firstName"/>
				<mx:DataGridColumn headerText="Last Name" dataField="lastName"/>
				<mx:DataGridColumn headerText="Email Address" dataField="emailAddress" width="200"/>
			</mx:columns>
		</mx:DataGrid>
		<s:HGroup>
			<s:Button label="Get Members" click="{getAllMembers();}"/>
			<s:Button label="Clear" click="{clearDataGrid();}"/>
		</s:HGroup>
	</s:VGroup>

</s:Application>

MemberData Value Object Class for Actionscript
This is the value object on the client side to define the field names for a member object. This is mapped to the server side on line 37 of the Zend AMF gateway script.

package
{
	/**
	 * Value object defining the member data
	 * */
	[RemoteClass(alias="MemberData")]
	[Bindable]
	public class MemberData
	{
		public var memberKey:uint;
		public var firstName:String;
		public var lastName:String;
		public var emailAddress:String;
	}
}

Zend AMF Gateway PHP Script
This is the gateway program for the Zend Amf. This is the file that is referenced on line 13 of the MXML file. In this example it was named index.php.

<?php
/**
*  Sample Zend AMF gateway
*  @return Endpoint &#91;Zend Amf Endpoint&#93;
* */

// Configurable values
// Debugging values
$debug = true;                             // Debugging status
if ($debug)
{
	// Report all errors, warnings, interoperability and compatibility
	error_reporting(E_ALL|E_STRICT);
	// Show errors with output
	ini_set("display_errors", "on");
}
else
{
	error_reporting(0);
	ini_set("display_errors", "off");
}
// Add the Zend AMF installation folder to the include path.
// In this example the frameworks folder is a sibling folder to
// this application folder. The frameworks folder contains the Zend
// folder that is extracted from http://framework.zend.com/download/amf
ini_set("include_path", ini_get("include_path") . PATH_SEPARATOR . "..\\frameworks" );

// Instantiate the Zend Amf server
require_once 'Zend/Amf/Server.php';
$server = new Zend_Amf_Server();

// Register your service classes
require_once 'MembershipService.php';
$server->setClass("MembershipService");

//Map ActionScript value objects to the PHP value objects.
$server->setClassMap("MemberData", "MemberData");

// Return the handle.
echo ($server->handle());

?>

[ad name=”Google Adsense”]
MembershipService Class
This is the service class that contains remote methods. Generally a the database and business logic is delegated to another API you write. In this case they are all together for simplicity.

<?php
/**
*	Service class exposing the methods to deal with membership.
*   This example includes business logic for simplicity of study.
*/
require_once 'MemberData.php';
class MembershipService
{
	public function MembershipService()
	{
		// Connect to MySql database.
		// Supply your own MySQL access values.
		// These are defaults when running on your own private computer.
		mysql_connect("localhost", "root", "");
		// Select the database.
		// Supply your own database name.
		mysql_select_db("test");
	}
	/**
	*	Get all members and all fields.
	*/
	public function getAllMembers()
	{
		// Array of MemberData objects.
		$members = array();
		// Selecting all fields and all records from table.
		// Supply your own table name and optionally your own SQL statement.
		$result = mysql_query("SELECT * FROM zend_amf_members");
		// Assuming mysql_query success. Slog through records.
		while ($row = mysql_fetch_assoc($result))
		{
			// Create a MemberData value object and populate.
			$member = new MemberData();
			$member->memberKey = $row["memberKey"];
			$member->firstName = $row["firstName"];
			$member->lastName = $row["lastName"];
			$member->emailAddress = $row["emailAddress"];
			array_push($members, $member);
		}
		// Return the members array to client.
		return $members;
	}
}
?>

MemberData Value Object Class for PHP
This is the value object on the server side to define the field names for a member object. This is mapped to the client side on line 37 of the Zend AMF gateway script.

<?php
/**
 * Value object defining the member data
 * */
class MemberData
{
  public $memberKey;	// uint
  public $firstName;	// String
  public $lastName;		// String
  public $emailAddress;	// String
}
?>

SQL To Create Testing Table
The PHP script uses zend_amf_members for the table and this is the SQL to create that table. In this example the database was called test.

SET SQL_MODE="NO_AUTO_VALUE_ON_ZERO";

--
-- Database: `test`
--

-- --------------------------------------------------------

--
-- Table structure for table `zend_amf_members`
--

CREATE TABLE IF NOT EXISTS `zend_amf_members` (
  `memberKey` int(10) unsigned NOT NULL auto_increment,
  `firstName` varchar(30) NOT NULL default '',
  `lastName` varchar(30) NOT NULL default '',
  `emailAddress` varchar(50) NOT NULL default '',
  PRIMARY KEY  (`memberKey`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1 COMMENT='Zend Amf Examples' AUTO_INCREMENT=7 ;

--
-- Dumping data for table `zend_amf_members`
--

INSERT INTO `zend_amf_members` (`memberKey`, `firstName`, `lastName`, `emailAddress`) VALUES
(1, 'Donald', 'Duck', 'quacker@pond.com'),
(2, 'Daffy', 'Duck', 'daft_2x@farm.org'),
(3, 'Elmer', 'Fudd', 'elmer.fudd@hunters.net'),
(4, 'Bugs', 'Bunny', 'whats_up_doc@underground.org'),
(5, 'Yosemite', 'Sam', 'varmint_chaser@forest.com'),
(6, 'Wile', 'Coyote', 'ceo@acme.com');