Building a structured RIA with Flex and PHP5\MySQL (episode 1)

Building a structured RIA with Flex and PHP5\MySQL

Purposes:
The application we’re going to build is quite simple, actually this tutorial main purpose is to understand the architecture of a structured Rich Internet Application.
We’re going to build a simple forum with these simple features:
- login\logout
- view the forum\view a login page
- data insert\retrieve
- create\load posts and threads

Involved open source projects:
- Cairngorm: a MVC microarchitecture for Flex (Adobe)
- ZendFramework: a PHP framework implementing MVC and other features such as AMF protocol (by Zend)
- Doctrine: a PHP ORM (by http://www.doctrine-project.org/about )

Audience:
This is not really a “from the ground up” tutorial, you need to be aware of what Adobe Flex and PHP5 are and have some basic notions on these technologies and common design patterns such as MVC.
However even if you don’t match the audience’s profile reading is not forbidden, you can take a deep dive into what your lacks are after this tutorial.

Episodes:
yes, episodes : )
I couldn’t call them “chapters” ’cause I’m not going to write a book about this topic, but for a better understanding I think it’s better divide this tutorial in parts pertaining to client or server side or a specific technology.

Episode 1

Overall view
First of all we need to take an overall look at the RIA’s architecture.
Our application is intended to be a simple forum, so let’s break down a forum into small atomic objects such as threads, posts.
Just for now let’s ignore other features such as users’ posts ownership, users authentication etc,
let’s simplify the app as more as possible.

DTOs (data transfer objects) and VOs (value objects)
DTOs (also known as value objects) are objects used to transport data between client and server. They’re implemented both on the client and server side to strongly couple each side of the application and to have a better coherence in handling data.
DTOs have no logic inside, they’re dumb classes used as data structure only.
In our example application Thread and Post classes are DTOs due to their nature:

  • they have no logic
  • they are atomic parts exchanged between client and server

Commonly we talk about VO on the client side and DTO on the server side, anyway this is just a naming convention and you can apply your own. A best practice is to name each DTO\VO class by appending its nature at the end of the class name. So our client side Thread class will become ThreadVO and Post class on the server becomes PostDTO.

Let’s take a look at the client implementation first.

Coding the client

Understanding the Cairngorm microarchitecture basics
Cairngorm implements its way to decouple models views and controllers letting the developer accessing them through some locator classes.
You can download Cairngorm binaries and source from opensource.adobe.com .
Let’s explore Cairngorm’s basics classes and interfaces.

Interfaces:
IModelLocator is a marker interface for model locator classes.
IValueObject is only a marker interface for VOs.
you can decide yourself whether to implement them or not (it really doesn’t matter, they only make your code much more readable for a human being, but)
ICommand is the interface your commands must implement to be Cairngorm compliant. It defines the “execute” method in which you can implement your own command by switching on the CairngormEvent type.

Classes:
CairngormEvent is the base class for events, you just have to extend it to make your own event to fit your purposes.
FrontController is the base controller class, its addCommand method associates a CairngormEvent to an ICommand.
ServiceLocator is the class you can extend to define the application business such as remote objects, http services, hardcoded data and so on.

Now let’s try to write a few lines of code : )
Create a new Flex project with PHP backend and name it “SimpleForum”. Target your PHP deploy folder as webroot on the wizard dialog.
Then get into the project’s library path dialog and add your Cairngorm.swc to the build path.
Now we’ve to create some directories in our src folder, just to have the Flex project organized.

Let’s create a folder for:

  • model
  • view
  • controller
  • commands
  • events
  • business

Now, let’s setup a few classes of our example application using Cairngorm.

Let’s start creating a new ThreadEvent class in the events package by extending CairngormEvent and let’s put a static constant named “THREAD_TEST” we’ll use to define a test event type for test purpose only.

?View Code ACTIONSCRIPT
package events
{
	import com.adobe.cairngorm.control.CairngormEvent;
 
	public class ThreadEvent extends CairngormEvent
	{
		public static const THREAD_TEST:String = "THREAD_TEST";
 
		public function ThreadEvent(type:String, bubbles:Boolean=false, cancelable:Boolean=false)
		{
			super(type, bubbles, cancelable);
		}
 
	}
}

Then we need a command to handle this test ThreadEvent, so let’s create a ThreadCommand class in the commands package implementing ICommand interface for that purpose.
As you can see an “execute” method accepting a CairngormEvent parameter is created.

Note: a good way of coding is to create a command class for each single event. For quite small projects this can be done pretty easily but as the things are getting bigger this solution will become unhandy and “file prolific”. So it’s a best practice to pair each event class (not type) with its respective command class.

Just to test let’s put an Alert right there in the execute method, afterwards we’ll implement some other ThreadEvent event types just by switching on event.type .

?View Code ACTIONSCRIPT
package commands
{
	import com.adobe.cairngorm.commands.ICommand;
	import com.adobe.cairngorm.control.CairngormEvent;
 
	import events.ThreadEvent;
 
	import mx.controls.Alert;
 
	public class ThreadCommand implements ICommand
	{
		public function ThreadCommand()
		{
		}
 
		public function execute(event:CairngormEvent):void
		{
			if(!(event is ThreadEvent))
				return;
			var te:ThreadEvent = ThreadEvent(event);
			switch(te.type){
				case ThreadEvent.THREAD_TEST:
					Alert.show("THREAD TEST WORKS!");
				break;
			}
		}
 
	}
}

Now we lack a controller to pair ThreadEvent and ThreadCommand, so let’s create a ThreadController class in the controller package by extending FrontController class.
FrontController provides a useful method which is addCommand, it actually associates an event type (a string) to a class implementing ICommand.
We need right that method, so we can pair ThreadEvent.THREAD_TEST and ThreadCommand class.

?View Code ACTIONSCRIPT
package controller
{
	import com.adobe.cairngorm.control.FrontController;
 
	import commands.ThreadCommand;
 
	import events.ThreadEvent;
 
	public class ThreadController extends FrontController
	{
		public function ThreadController()
		{
			super();
			addCommand(ThreadEvent.THREAD_TEST,ThreadCommand);
		}
 
	}
}

Now we can test our Cairngorm test:
create a new button labeled “test” on your application mxml file, create a new “test” function and put it on the button’s click handler.
Create and dispatch a new ThreadEvent in the test function by writing

?View Code ACTIONSCRIPT
	new ThreadEvent(ThreadEvent.THREAD_TEST).dispatch()

Finally we can put a ThreadController mxml tag on our application file.

Note: Cairngorm events are a little different from standard events. They need to be explicitly dispatched by running the dispatch() method, otherwise they’re just created but won’t dispatch. Moreover, a FrontController is needed as it acts as an event listener does for standard events.

<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" layout="absolute" xmlns:controller="controller.*">
<mx:Script>
	<![CDATA[
		import events.ThreadEvent;
		private function test():void
		{
			new ThreadEvent(ThreadEvent.THREAD_TEST).dispatch();
		}
	]]>
</mx:Script>
	<mx:Button label="test" click="test()"/>
	<controller:ThreadController/>
</mx:Application>

Now let’s compile and run our application and click the test button. If you achieve an alert everything’s ok, otherwise let’s start debugging.
Cairngorm makes it a lot easier; if you expect your application doing something and nothing happens just take a look to each step:

  • do you call your CairngormEvent.dispatch() method?
  • if yes, does your application include a FrontController?
  • if yes, does that FrontController pair the CairngormEvent you dispatch and an ICommand?
  • if yes, do you handle that CairngormEvent type on your ICommand.execute method?
  • if yes you can start a real debug session, but the most of the times you’re done after 2 or 3 steps : )

Now that Threads are done try yourself to make a working MVC loop for posts.

Working with data
Now we’re re ready to work with data models for posts and threads.
First of all let’s define what properties are going to be within our models.

Thread would have an ID, a author, a title, and a list of posts.
Post would have an ID, a thread id back reference, a author, a title and a body.

According to what we said before on DTOs\VOs let’s create two classes for our threads and posts, respectively ThreadVO and PostVO.
As you can see in the code both classes belong to the model package, further both implement IValueObject and no logic is defined in there.

?View Code ACTIONSCRIPT
package model
{
	import com.adobe.cairngorm.vo.IValueObject;
 
	[Bindable]
	public class ThreadVO implements IValueObject
	{
		public var id:int;
		public var title:String;
		public var author:String;
		public var posts:Array;
 
		public function ThreadVO()
		{
		}
 
	}
}
 
 
package model
{
	import com.adobe.cairngorm.vo.IValueObject;
 
	[Bindable]
	public class PostVO implements IValueObject
	{
		public var id:int;
		public var threadID:int;
		public var author:String;
		public var body:String;
		public var title:String;
 
		public function PostVO()
		{
		}
 
	}
}

Now we need to save somewhere our models as they’re loaded from the server (or, as right now, hardcoded), so let’s create a ForumModelLocator class implementing IModelLocator in the model package.
ForumModelLocator has to follow the Singleton pattern, or better, you’ve to make you sure to always access the same instance, otherwise its locator functionality is tragically lost.

Just for now let’s create only one public bindable property in there: an ArrayCollection named “threads”.

?View Code ACTIONSCRIPT
package model
{
	import com.adobe.cairngorm.model.IModelLocator;
 
	import mx.collections.ArrayCollection;
 
	public class ForumModelLocator implements IModelLocator
	{
		[Bindable]
		public var threads:ArrayCollection;
 
		private static var _instance:ForumModelLocator;
 
		public function ForumModelLocator()
		{
			if(_instance)
				throw(new Error("ForumModelLocator is a Singleton!"));
 
			//init
			threads = new ArrayCollection();
		}
 
		public static function getInstance():ForumModelLocator
		{
			if(!_instance)
				_instance = new ForumModelLocator();
			return _instance;
		}
 
	}
}

Now we’ve better to use those VOs and model locator with some data, otherwise it would be a waste of work.
In order to retrieve data from “somewhere” we’ve to introduce a new business unit to handle the asynchronous retrieval, this kind of “things” are called Delegates.
Delegates classes are strictly connected to commands.
The standard behaviour for command classes which are called to load\send some data from “somewhere” will register two methods (onResult and onFault) in a mx.rpc.Responder object (beware on the package). This Responder object is used as parameter for a delegate class which calls its specific service (a remote object, a webservice, hardcoded data, etc), and calls back the Responder method according to the result of the asynchronous operation.

Now that (I hope) the task is clear let’s create a test delegate and let’s try to integrate it with our previous MVC test loop.

Create a new ThreadDelegate class in the business package, in there create a private _command variable typed IResponder. Then let ThreadDelegate constructor accept and require an IResponder parameter and register it in the proper class variable.
Afterwards create a “threadTest” public method and put some hardcoded data in the _command.result parameter just for testing.

?View Code ACTIONSCRIPT
package business
{
	import model.ThreadVO;
 
	import mx.collections.ArrayCollection;
	import mx.rpc.IResponder;
	import mx.rpc.events.ResultEvent;
 
	public class ThreadDelegate
	{
		private var _command:IResponder;
 
		public function ThreadDelegate(command:IResponder)
		{
			_command = command;
		}
 
		public function threadTest():void
		{
			// hardcoded data
			var threadCollection:ArrayCollection = new ArrayCollection();
			var thread:ThreadVO = new ThreadVO();
			thread.id = 1;
			thread.author = "pigiuz";
			thread.posts = [];
			thread.title = "Hardcoded thread";
			threadCollection.addItem(thread);
			//
 
			// fake responder result
			_command.result(new ResultEvent(ResultEvent.RESULT,false,true,threadCollection));
		}
 
	}
}

Now we’ve to edit the ThreadCommand class in order to call this new delegate and put retrieved data into ForumModelLocator threads property.
So, first of all we’ve to remove the Alert.show, than we create our two response methods onThreadTestResult and onThreadTestFault and pass them to a new ThreadDelegate instance wrapped within a Responder object.

NOTE: Responder class is quite similar to Loader or URLLoader class but it’s simpler, it has only two behaviours (result and fault) instead of “n” different event handlers.

Now just run threadTest() method to “load” hardcoded data from ThreadDelegate and, as data comes let’s loop in there and add each thread to ForumModelLocator threads ArrayCollection unless an error is captured (but you know…it’s difficult to get an error with hardcoded data : ) )

?View Code ACTIONSCRIPT
//changes in ThreadCommand class
package commands
{
	import business.ThreadDelegate;
 
	import com.adobe.cairngorm.commands.ICommand;
	import com.adobe.cairngorm.control.CairngormEvent;
 
	import events.ThreadEvent;
 
	import model.ForumModelLocator;
	import model.ThreadVO;
 
	import mx.rpc.Responder;
	import mx.rpc.events.FaultEvent;
	import mx.rpc.events.ResultEvent;
 
	public class ThreadCommand implements ICommand
	{
		public function ThreadCommand()
		{
		}
 
		public function execute(event:CairngormEvent):void
		{
			if(!(event is ThreadEvent))
				return;
			var te:ThreadEvent = ThreadEvent(event);
			var td:ThreadDelegate;
			switch(te.type){
				case ThreadEvent.THREAD_TEST:
					td = new ThreadDelegate(new Responder(onThreadTestResult,onThreadTestFault));
					td.threadTest();
				break;
			}
		}
 
		private function onThreadTestResult(e:ResultEvent):void
		{
			// success
			for each(var thread:ThreadVO in e.result){
				ForumModelLocator.getInstance().threads.addItem(thread);
			}
		}
 
		private function onThreadTestFault(e:FaultEvent):void
		{
			// fault
		}
 
	}
}

Finally we have our data loaded, centralized in our ForumModelLocator and the test function running.
Now it’s time to create a component in the view package, a simple Panel based one repeating the data stored in the model locator.

<?xml version="1.0" encoding="utf-8"?>
<mx:Panel xmlns:mx="http://www.adobe.com/2006/mxml" layout="vertical" title="Threads">
	<mx:Script>
		<![CDATA[
			import model.ForumModelLocator;
			import events.ThreadEvent;
			private function test():void
			{
				new ThreadEvent(ThreadEvent.THREAD_TEST).dispatch();
			}
		]]>
	</mx:Script>
	<mx:Repeater width="100%" height="100%" id="threadsRepeater" dataProvider="{ForumModelLocator.getInstance().threads}">
			<mx:VBox width="100%">
				<mx:Label text="{threadsRepeater.currentItem.title}"/>
			</mx:VBox>
		</mx:Repeater>
		<mx:ControlBar>
			<mx:Button label="test" click="test()"/>
		</mx:ControlBar>
</mx:Panel>

And then we’re getting a pretty clean Application by putting there only the needed mxml tags:

  • the ThreadViewer
  • the ThreadController
<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" layout="absolute" xmlns:controller="controller.*" xmlns:view="view.*">
 
	<view:ThreadViewer 
		verticalCenter="0" 
		horizontalCenter="0"
		width="50%"
		height="50%"/>
 
	<controller:ThreadController/>
 
</mx:Application>

…and that’s it. Nice one isn’t it? : )

Download this episode source here

Don’t miss next episode:
retrieving\sending data through ZendAMF

One Response to “Building a structured RIA with Flex and PHP5\MySQL (episode 1)”


Leave a Reply