Since the first time I really saw and understood what Flash was and did, I've been jealous of what Flash designers could do. Beyond just the cute animations for banner ads and games, Flash designers have always had a much more fine-grained control on their environment that HTML allows for. Besides my innate inability to design anything like a usable interface, the main drawback that has kept me from exploiting Flash is I never could get my head around the "Movie Timeline" metaphor for programming. As we say back home, "It just ain't right."

That all changed with Flex. Now programmers can work with tools they are comfortable with and still take advantage of all of Flash's...well, flash. Flex won't help developers like me design eye-pleasing interfaces any more than a new pencil would improve my inability to draw. However, I can now put much more useful interfaces on my back-end code. That brings us to our topic, using Flex as a front-end for PHP web services. More specific, we will be using PHP and the Zend Framework as the back-end data source for a Flex application.

The complete source code for this project can be found here.

I love useless demo applications. It keeps me from worrying about what I'm building and allows me to concentrate on how I'm building it. The demo I'm presenting here, a Feed Reader for DevZone, is just such an application. It's almost wholly useless in and of itself but it allows me to showcase one way of using Flex, PHP and the Zend Framework together. If you want to see a more serious Feed Reader, check out http://dougmccune.com/flex/zomgzrss/. Now that's just cool.

What Makes the Frame-Work?

The Zend Framework is a PHP 5 based library of classes, many of which can be used stand-alone. Used together it is an excellent base for MVC based applications. For the purposes of our application, we will be using it to encapsulate all of the business logic in our applications. We will use Flex for the display logic.

There are many good frameworks available for PHP right now and you may want to try out several of them to find the one you like working with the most. The Zend Framework is a bit different from others available for PHP in that it is a component framework instead of an application framework. I like to tell people it's more of a toolbox than a specific tool. It gives you all the tools necessary to get just about anything done; it's up to you though to use them to build your application. In our case, this gives us some freedom that other frameworks may not.

As I am writing this, the Zend Framework is in the Beta cycle and is currently at version 0.9.3. It is probable that by the time this article is released version 1.0 will have been released. The code we will be looking at is all based on 0.9.0+. Since the API was frozen at 0.9.0, everything in this demo should work with 1.0 as well.

Assumptions

To execute this demo you will need the following.

  • Apache Web Server w/mod_rewrite installed
    The Zend Framework uses mod_rewrite to parse URLS. The current version of the framework also contains code that does not require mod_rewrite but for this demo, we do.
  • PHP 5.2.1
  • Zend Framework 0.9.0+
    Any version past 0.9.0 should have no problem working.
  • Flex 2
  • A Flickr API key.
    If not, visit this link and get yours for free
  • A Yahoo App ID.
    If you don't, visit this link and get one.

I developed this application on Windows XP using Zend Core. If you don't already have a working development environment setup, Zend Core is the easiest way to get started.

You can download the Zend Framework from the homepage. Make sure you uncompress it into a directory in your PHP include path.

You can use the free Flex SDK to build and debug Flex applications, but I prefer to use Flex Builder, an Eclipse based IDE. It's easier and quicker than coding it by hand and using command line tools. If you don't have it already, you can download a 30 day trial of Flex Builder from Flex.org.

I'll leave it to you to hook all the pieces together in your particular working environment.

All of the tools necessary to work through this demo are available on Linux, OS X and Windows. For the purposes of the demo, if there is anything OS specific (pathnames, separators, etc) they are specified for Windows.

What Does the Big Picture Look Like?

Ok, one last thing before we dive into the code and swim around. Let's look at exactly what we are building. I call this project MashupTimes. It is a RSS Feed Reader for DevZone that adds pictures from Flickr to each article. (For those of you who do not know, DevZone is Zend's community news web site.) I chose DevZone for two reasons. First and foremost, DevZone publishes entire articles in its feed. This means to display and work with the contents of the article, we don't have to parse any HTML. The second and more obvious reason is to pimp DevZone to any of you who don't read it daily.

Many people have written feed readers in Flex though, why another one limited to just one feed? One of the main tools of Web 2.0 is tagging. Everybody is tagging everything these days. I wanted to have a little fun with tags and see what we could discover. Also, I wanted to show how easy it is to string things together using the Zend Framework and mash-up several sources to create something new.

So, we will take the content from a DevZone article and work with it. The first thing we do is pass it through Yahoo's Content Analyzer and let it pull out the keywords for us. Now we have tags we can work with. We will now take our tags and ask Flickr for its most interesting photo for each tag. We will then display the list of photos below the article. The end result looks something like Figure 1.

Figure 1: Screen shot of DevZone Feed Reader

On a side note, it's always interesting to me when I start working with Flickr to see what images will come up for specific tags. Usually, it's very entertaining, sometimes a bit risque but they almost never have anything to do with the article itself.

So that's what we will be doing. On the PHP side we will explore setting up a Zend Framework application, using components to fetch information from providers and caching. On the Flex side we will explore bringing it all together.

Business in the Back: The PHP Side

Ok, here's the part you've been waiting for, let's dive into some code. I won't attempt to show every line of code I wrote for this project, you can download the source and take a peek at the pieces individually. My goal here is to highlight the pieces of code that implement an important concept.

First, let's set things up. I'm a firm believer of the PHP security principal of putting only the bare essentials inside your web root. For this project the PHP directory structure looks like this:

/mashuptimes
	/www
		/flash
	/phpinc
		/Output
		/settings
	/app
		/models
		/views
		/controllers

The beneath the root of our project we have the 3 main directories. www is our web root. There are only 3 files in this directory. Because the Zend Framework can use mod_rewrite to handle processing the URL, we have an .htaccess file. Next we have index.php. Index.php is the Zend Framework's "bootstrap" file. It sets everything up, instantiates the front controller and hands off processing to it. Finally, in the flash subdirectory we have all the files that Flex generates to display and run the application. We also have an .htaccess in that directory that turns off mod_rewrite for it. Otherwise, it would try and interpret all the calls to the flash app and send them through our bootstrap.

phpinc is the application specific php include directory. Anything that is custom for this application will go in there.

app is where our Zend Framework based code will go. For consistency's sake, I've defined models, views and controllers but in this application we are only using controllers.

In addition to these, as I've mentioned before, you will need to make sure you have the Zend Framework installed and accessible to PHP.

Let's Bootstrap

The heart of any Zend Framework application is the bootstrap file. If you don't get this one right, nothing is going to work. There are two important sections in our bootstrap file. The first section worth noting is the building of the include path.

$lib_paths = array();
$lib_paths[] = "c:/web/mashuptimes/app";
$lib_paths[] = "c:/web/mashuptimes/phpinc";
$lib_paths[] = "c:/web/zendlib/incubator/library";
$lib_paths[] = "c:/web/zendlib/library";
$lib_paths[] = "c:/web/pear";

$inc_path  = implode(PATH_SEPARATOR, $lib_paths);
set_include_path($inc_path);

Since this is demo code and not a production application, we can get away with writing code like this because it makes changing things easy. However, you would never want to do this in a production environment.

The other important function of the bootstrap is to instantiate the front controller and get things moving.

Zend_Loader::loadClass('Zend_Controller_Front');
$frontController = Zend_Controller_Front::getInstance();
$frontController->throwExceptions(true);
$frontController->     
     setControllerDirectory('c:/web/mashuptimes/app/controllers/');
$frontController->dispatch();

As you can see we specifically tell the front controller where to find the other controllers in the application and then just fire dispatch(). That's it for the bootstrap. Once those lines are in place, everything else should work just fine. Now let's jump into the heart of the PHP code, the ApiController.php file.

As a convention, the Zend Framework expects all controllers to end with the word Controller and all actions to end with the word Action. Thus the URL

http://mashuptimes.local/main/doitnow

would cause the front controller to find MainController.php and within it the method doitnowAction(). For the purposes of our application all URLs are expressed this way.

http://sitename/controller/action/parameter1/value1/parameter2/value2

You can have as many parameter/value pairs as you need on a URL, HTTP limits not withstanding.

Who is in Control?

Since the ApiController is the main piece of the PHP code necessary for the back-end of our application, we are going to take apart a few of its piece and dive into some of the concepts implemented.

In the __construct of ApiController, we instantiate three filters.

$this->safeStringFilter = new Zend_Filter();
$this->safeStringFilter->addFilter(new Zend_Filter_StripTags())
                       ->addFilter(new Zend_Filter_StringTrim());
$this->digitsFilter = new Zend_Filter_Digits();
$this->alphaFilter  = new Zend_Filter_Alpha();

Since it is not safe to assume that because this API is meant to be called by another application it will only be called that way, we need to filter any and all input coming in from the user. The 3 filters we instantiate are simple and good enough to meet the needs of our application. We also instantiate a cache controller in the __construct().

$this->cache = Zend_Cache::factory('Core',
                                   'File',
                                   array('lifeTime'=>60,
                                       'automaticSerialization'=>true),                                   array('cacheDir'=>Globals::$CACHE_DIR));        

As you will see later in this article, we cache a lot of discrete pieces of information.

The first action in the controller is getfeedAction(). getfeedAction() does just what it's name suggests, it fetches the current feed from DevZone and output's it to the Flex application. Let's dive into the details of how it does what it does and more importantly, why.

In this first section we setup some variables that we will use throughout the rest of this action.

$feedUri     = 'http://devzone.zend.com/public/view/format/rss2.0';
$returnValue = array('entry'=>array());
$cacheKey    = md5($feedUri);
$count       = $this->digitsFilter->
     filter($this->_getParam('count'));
$count       = $count>0?$count:5;

As you can see we pull in the parameter count passed in on the URL. We pass this through our digits filter as it should never be anything but an integer. This is the number of articles the front-end has requested we return.

...and now a word regarding caching

Next we check and see if we already have this feed cached.

if (!$returnValue=$this->cache->load($cacheKey)) {

Each action in this application caches the information it retrieves from our service partners. Different information is cached for different time frames. In the case of the feed, we cache it for 60 minutes.

Caching provides two benefits. First, retrieving from cache greatly reduces latency in the application. Since we are not actually calling the service partner's site, we are pulling from our local store. For some partners like Flickr, this can be a tremendous savings.

Second, it insulates us from our service partner's outages. All applications will experience an outage at some point. By storing the information we request in the cache, we are insulated from them.

The important thing to think about when caching is how long to cache. Some information, like the feed from DevZone, changes but not often. In this case, 60 minutes is an acceptable cache life. In the case of the keywords for an article however, we cache those for 48 hours. Yahoo's Content Analysis service has been around for a while and is stable. The chances of it changing it's mind and giving us a new set of keywords for an given article are very slim. So why bother Yahoo by asking it the same question over and over. Finally, Flickr responses are cached for 6 hours. Given the number of photos that get uploaded daily, there is a good chance that Flickr will pick a different picture for a given keyword day to day. So we hold onto it for a while but not forever.

The Zend_Cache class requires a unique identifier for each discrete piece of information. The 'cacheKey' can only be alpha-numeric characters. To adhere to this rule and minimize the possibility of collisions, each piece of information is assigned a cache key that is an md5 of a unique identifier of that piece of information. In the case of the feed, it's the URL, in the case of the images retrieves from Flickr, it's the keywords. I'll leave it to you to review the code to discover the keys actually used.

Assuming it's not cached, we actually do go fetch the feed.

$feed = new Zend_Feed_Rss($feedUri);

This is another place where the Zend Framework really shines. A single line of code will fetch the feed for us.

foreach ($feed as $item) {
$newsCacheKey = md5($item->link);
$newsItem = array('title'       => $item->title(),
                        'link'        => $item->link(),
                        'pubDate'     => $item->pubDate(),
                        'category'    => $item->category(),
                        'description' => $item->description());
      $this->cache->save($newsItem,$newsCacheKey,array(),
                   (60*60*48));
      $returnValue['entry'][] = $newsItem;
} 

This for loop, directly below the call to Zend_Feed_RSS does the parsing and prepares the array for us. This is a common construct that you will see in each of the actions. In all cases, we take the input from the service provider and convert it internally to an array.

To demonstrate the flexibility of building the back-end in this manner, I wanted to show that we could easily repurpose the API we are building and feed a regular web application. So the type of output requested is specified on the URL. In the code of the Flex application we will be requesting XML because Flex has strong XML parsing tools built in. However, if we were handing this off to a web page we could just as easily request JSON.

Currently, those are the only two output types available. However, as we will see when we look at the output processors, other formats could easily be added. (YAML, CSV, etc.)

Inside the "for loop" you will notice that we are making calls to the cache object to save off each individual article. This is the only action in the controller that caches multiple pieces of information. In this case, we cache each article separately so that we can easily retrieve it based on the articles URL.

The final step of breaking things apart is to store the entire array in the cache for later use. Even though we allow the user to specify how many articles to return, we process and store the entire feed. This way if the count changes later, we don't have to rebuild the cache.

...and now for the output

$outputType     = 
$this->alphaFilter
     ->filter($this->_getParam('outputType'));
$processorClass = $this->getProcessorClass($outputType);
$processor      = new $processorClass($this->response);

Finally, we prepare the output for display. As we discussed earlier the user can specify the output type in the request. For security's sake, we filter the value before we attempt to use it. The controller has a protected method getProcessorClass that will take the output and locate the proper class for us. Once we have the proper class, we instantiate it with the array we built as the only parameter.

All output processors are built subclasses of Output_Abstract. For security and convention's sake, they are all located in a directory Output located somewhere in your PHP include path. (In this case, Output is located in /mashuptimes/phpinc) Output_Abstract defines the pieces and one of the methods but leaves the subclasses to implement the important method, main(). Main is called in _construct() and will take the passed in array and process it building a string representation of the requested output in $this->payload. Once main() is complete, processing is handed back to the controller to finish up.

$this->send($processor);
return;

The final task is to hand the payload back off to the processor to send to the browser. ApiController has a protected method named send() that does just that. It takes the processor as it's input parameter and uses it to setup what goes back to the browser.

Since each type of output may need one or more custom http headers, send() checks the output processor and sets any headers that have been requested. In the case of Output_XML the Content-Type header is set to text/xml.

It finally hands the payload we've generated off to the Request object so that the front controller can output it.

$response = $this->getResponse();
$response->appendBody($processor->getPayload());

That's really it for the PHP side of things. There are two other actions in ApiController:

  1. getKeywords
  2. get Photo

You can examine their code at your leisure but you will see that they both operate similarly. The thing worth noting are how each method talks to it's appropriate service provider.

In the case of Yahoo's Content Analysis service, there is no specific class to use in the Zend Framework. However, since it's a REST API, we can simply use Zend_Rest_Client to make our requests to it. Since we are handing the service a large block of text, potentially larger than the HTTP protocol can handle via a GET, we use POST to talk to it.

$uri     = 'http://search.yahooapis.com';
$service = '/ContentAnalysisService/V1/termExtraction';
$request = array('appid'   => Globals::$YAHOO_APID,
                 'context' => $article['description'],
                 'output'  => 'xml');
$rest    = new Zend_Rest_Client();
$rest->setURI($uri);
$response = $rest->restPost($service, $request);        

As you can see making this request a POST is as easy as calling restPost(). All the other parameters remain the same.

In the case of Flickr, we use Zend_Service_Flickr. Zend_Service_Flickr allows us to set all of the parameters that Flickr will allow and retrieve just the photo we want. Since we are doing a simple sorted search, it only takes 3 lines of code to make the call.

$flickrSearchOptions = array('per_page'=>1,
     'page'=>1,
     'sort'=>'interestingness-desc'); 
$flickr = new Zend_Service_Flickr(Globals::$FLICKR_APID);
$result = $flickr->tagSearch($keyword,$flickrSearchOptions);

$result contains the XML payload from Flickr that we will parse and cache.

Party in the Front: The Flex Side

Now on to the Flex side of things, the front-end. As I stated in the opening, not being a very good UI programmer, I tend to ignore it and concentrate on the middle-ware and the back-end. It's just easier for me to think in those terms. I've worked in many languages building desk-top applications where I had to build the front-end for my code and I can honestly say that Flex Builder from Adobe is one of the best front-end IDEs I've worked in. This project didn't call for it but with Flex being able to easily build Apollo applications, you can now use it to build desktop applications that work cross-platform.

Flex Builder is based on Eclipse and available in either stand-alone or as a plug-in. If you are used to working in Eclipse, you should have no problem working in Flex Builder. Beyond that though, the interface just makes sense. It's very easy to move between design and source and changes made in one are immediately reflected in the other.

The core language that Flex projects are coded in is ActionScript. MXML is the layout description language behind Flex. It used to layout the application, similar to XUL or XAML. If you've worked in JavaScript at all you'll recognize a lot of ActionScript. There have been some improvements though. One thing PHP developers will notice right away is that all methods in ActionScript have to declare their return type and the types of the parameters. Having worked in a loosely typed language for so long, I think this is the thing that tripped me up the most.

Beyond that though, it's just a matter of learning the syntax. That's where Flex Builder's Help function comes in. I almost always had it open in another window so I could research the language constructs.

As before, let's discuss how things are setup a bit first. Flex, like PHP requires a few directories to get things done. Here's how my Flex project is laid out.

/mashuptimes 
	/assets
	/mashuptimes
	/html-template

These are the standard directories that Flex sets up when you create a Flex project. I created the Flex project inside the root directory of my project just for convenience. A better idea would have been to have a subdirectory just for the Flex project under the project root.

I also configured the compiler to compile into /mashuptimes/www/flash and to use the URL http://mashuptimes.local/flash/mashuptimes.html when browsing. This way, when I'm done, everything is in the right place to run. These compiler options can be found on the Flex Build Path of the Project Properties dialog.

This being my first project in Flex, I did what any other programmer would do, I looked at a lot of source code from other developers. Adobe made it easy for developers to share their code. There is an option in the Project menu to "Publish Source". You can select the specific files you want to make available or just publish it all.

4 Controls: 3 Major, 1 Minor

After much though, coding, recoding, and then recoding again, I finalized the design that I will show you. It's not necessarily the best way or even the only way to get the job done, it's just the way I chose. The design centers around three main controls.

Fetching and Showing the Data

The first, a data grid is easily visible on the left had side of Figure 1. It is the main navigational element of the project. It's events are the ones that trigger almost all actions in the application. The one action that can be triggered by another control is the "Refresh Feed" button, the minor control. We will talk more about it later.

Since our data grid will contain the titles of the articles from our feed, we have to create an HTTPService to talk to our server and hand the data off to the DataGrid.



If you are new to Flex then the first thing you probably noticed is that defining controls is all done in XML. Actually everything in the MXML files is valid XML. This constitutes the bulk of our code. For PHP developers, this will take a bit of getting used to but once you make it past that hurdle, everything else starts to fall into place.

The one thing that is missing on the definition above is the URL of the service to access. Because I wanted the host URL to be defined as a reusable parameter, we don't set the URL parameter in the definition. We set it in the appInit() code shown below.

public function appInit():void
{
	this._host = "mashuptimes.local";
	this._uri  = "http://"+this.host;
	dzSrv.url= this.uri+"/api/getFeed/outputType/xml";

	_currentArticle=-1;
	_articles = new ArrayCollection();
	dzSrv.send();
	return;
}

The appInit() code, called from the Application object's creationComplete event, which is automatically triggered once everything is loaded, starts the chain of events that populates our UI by invoking dzSrv's send() method. This makes the call to the server to retrieve the feed. Once we have a result back from the server we fire the method gotData(). Since everything we are dealing with so far is scoped to the application, inside of gotData we can access the raw data.

public function gotData():void
{
	_articles = new ArrayCollection();
	for each (var item:* in dzSrv.lastResult.root.entry.entry)
	{
		_articles.addItem(new FullItemDetail());
	}
	entries.selectedIndex = 0;
	changeArticle(entries.selectedIndex);
	return;
}

In the case of gotData() we don't return anything so void is the proper response.

The way the application works is simple. We request the feed from the server. (Our PHP server, not DevZone). Then we spin through the result set using the for each loop and instantiate an instance of FullItemDetail for each article in the feed. We add these to an array _articles but we don't load them with data just yet. Once all the articles have FullItemDetails in place, we fire changeArticle() with the article id of 0 to load the data for the first article and display it.

public function changeArticle(index:int):void
{
	if (_currentArticle>-1) {
		_articles[_currentArticle].visible=false;
	}
				
	if (!_articles[index].isLoaded) {
_articles[index].loadDocument(
dzSrv.lastResult.root.entry.entry[index]);
		p.addChild(_articles[index]);
	}
	_articles.getItemAt(index).visible = true;
	_currentArticle = index;
	return;
}

changeArticle() takes as a parameter the index in the array to switch to. The logic is simple enough.

  • It turns the current one invisible, if there is one visible
  • It checks the one being requested to see if we've already loaded the data. If not, it fire the loadData() for it.
  • Add it to the panel as a child
  • It makes it visible

This is the same method that is called from the change event of the DataGrid as well. Any time the user requests a change in the article being viewed; this is the method that handles it. One important note here that I don't want to gloss over. The Flex Builder help file has an entire section on "Display Programming". Since the end result of any Flex application is a .swf file, this is important. When building display classes programmatically, you have to add them to the "Stage" before they will actually be visible. In the method above, we add the FullItemDetail objects to the panel after we have invoked loadData(). Since this is inside the IF statement, it is only done once. From then on we can just set visible to true or false to control them. You will see in the other classes we will look at every visual control is added to the Stage at some point.

Beyond those two methods, the only other logic in the main file is the refreshFeed() method. This is called on the click of the button. This has to do the equivalent of a total application cleanup before we can re-initialize everything. This means we need to destroy every FullDetailItem we created. This is a little more complicated than it sounds. Cleaning the array is simple. But before we can clean the array, we need to remove all the FullItemDetail children from the Panel.

protected function refreshFeed():void
{
	var lcvA:int =0;
for (var lcvA:int=p.numChildren-1;lcvA>=0;lcvA--) {
if (p.getChildAt(lcvA) is FullItemDetail) {
			p.removeChildAt(lcvA);
		}
	}
				
	_articles.removeAll();
	appInit();
				
}

Starting from the bottom of the list of children, we count backwards. If the child we are looking at is a FullItemDetail then we remove it using the removeChildAt() method. Once they are all removed, we can execute the removeAll() method on the array of articles, thus destroying all references and instances. Finally, we fire appInit() to start all over again. In case it is not obvious, we can't just destroy all children of the Panel because the DataGrid (entries) and the HTTPService (dzSrv) are both children of the panel and we need them to re-initialize the application.

From a PHP perspective, the following method may seem a bit unusual. The application has a private property named _host. The end result we are shooting for is to be able to use

public function get host():String
{
	return this._host;
}

x=this.host;

In PHP, we would simply overload the __get and put code in there to properly handle the this.host property. However, in Flex, you have the concept of a true GETter and SETter. These are denoted on the function definition line. You cannot have a property "host" and a public function get host(). So I fell back to the PHP convention of protected and private variables were named with an _. This allowed me to define the GETter normally and use it in the manner I described.

That's all there is in the main MXML file. You can look at the definitions for the other visual objects but there are no new concepts to discuss. Let's turn our attention to FullItemDetail.

Show Me the Detail!

FullItemDetail is both a display class and the encapsulation of the code necessary to handle all the processing. To make it displayable, we subclass the Canvas class, this will allow us to attach it to the Stage as well as attach children display object to it. In Flex, similar to PHP 4, classes have constructors that are methods named the same as the class. (Trust me when I say, that's where the similarity ends.) Inside the constructor of FullItemDetail we create several other class members.

  • _detailTitle:Text
  • _detailText:TextArea
  • _pix:HBox
  • _keywordRequest:HTTPService

Since FullItemDetail is an ActionScript class and not a typical display class, we have to create these items manually instead of just dragging them into the design surface. It is also incumbent upon us to properly position them if necessary and add them to the Stage. Here's an example :

_pix = new HBox();
_pix.y = 400;
_pix.x = 5;
_pix.width=415;
_pix.height=115;
_pix.horizontalScrollPolicy='auto';
this.addChild(_pix);

_pix will be the container we add all the images from Flickr to.

When changing articles, as we saw when reviewing changeArticle(), the parent object invokes loadData() on the FullItemDetail. Here's the code that is actually executed at that point.

public function loadDocument(rowToLoad:Object):void
{
	_keywordRequest.send({articleUri:rowToLoad.link});
	_detailTitle.text=rowToLoad.title;
	_detailText.htmlText=rowToLoad.description;
	this._isLoaded=true;
	return;
} // public function loadDocument(rowToLoad:Object)

We hand in an object created from the XML. From that we extract the Uri and send that back down to the server as part of the keywordRequest. Handing down the Uri instead of the entire body saves on bandwidth. However, in both cases, we are passing information that cannot be passed by GET. Since a URL contains slashes, "/", it cannot be passed as a normal parameter in the query string of the URL. This is not a problem. When we instantiated our _keywordRequest object, one of the parameters we set was method.

_keywordRequest.method="POST";

Thus the articleUri parameter will be part of a POST not a GET. This information important for the back-end to know as it has to use a different method to retrieve the value.

One thing you will notice when looking at the code for FullItemDetail is that for the _keywordRequest item, we also had to add an event listener. We want code to execute upon completion so we execute this line:

_keywordRequest.addEventListener("result",this.processKeywords);

Honestly, this is one of the mundane uses of addEventListener. ActionScript is highly event driven and you can create events and add listeners for just about anything. It really opens up some possibilities.

The code above tells the system to execute this.processKeywords when we get the list of keywords back from the server. processKeywords() is the most difficult task that executes in FullItemDetail so let's take a quick peek at the code.

private function processKeywords(event:Event):void
{
for(var thisKeywordIndex:String in 
_keywordRequest.lastResult.root.keyword.keyword)
	{
		var thisKeyword:String = 
_keywordRequest.lastResult.root.keyword.keyword[thisKeywordIndex];
		var newImage:Picture = new Picture();
		_pix.addChild(newImage);
		newImage.send(thisKeyword);
	}
	return;
} 

_keywordRequest will get back an XML payload of single keywords. What we want to do here is request a picture for each keyword. To encapsulate this code and keep things readable, we create an instance of the Picture class for each keyword. We'll look at the Picture class next. Once instantiated, we add it to the _pix HBox which serves as the container to hold it as well as (for lack of a better term) the "visual marshaler". We don't know how many keywords that Yahoo will send back to us. We do know that we want to display them all and since we are not working in a web page, we can't just scroll vertically for ever. The HBox container handles that for us. Each visual item added will be added at the end of the line. Once there are more children visible than can be displayed, a scroll bar is displayed automatically for us.

The final thing that processKeywords() does is to invoke Picture.send(). Picture has it's own HTTPService so it can talk directly to the server to request the URL of the image.

See the Pretty Picture

The final piece of the puzzle on the front-end is the Picture class. Unlike the FullDetailItem class, the Picture class is not an ActionScript class, written exclusively code; it is a MXML component. This allows us to use the visual editor to define the class and its children.

The Picture class has 2 children, the HTTPService we mentioned already and a Text field, unimaginatively named text. Text is where we display the keyword for the picture. The two methods it encapsulates are send() and processPicture(). Send, as we've discussed, simply invokes the send() method of the HTTPService, pictureRequest. Upon completion, it invokes processPicture()

private function processPicture():void
{			
    if (pictureRequest.lastResult.root.error.code==0) {
        image.source=pictureRequest.lastResult.root.square;
        text.text = pictureRequest.lastResult.root.keyword;
        this.visible=true;
    }
    return;
}

We check to make sure that the server didn't return an error code to us. Believe it or not, there are some keywords that Flickr doesn't have picture for. If we got something back then let's show it. We add the URL to the image source, add the keyword to the text's text field and we set visible to true.

Picture contains three "housekeeping" methods, recomputeSize(), setWidth() and setHeight(). They all do exactly what they sound like they do. I'll leave it to you to examine the project's sourcecode should you want to review them.

fClose()

That's it. Using Flex as the front-end and the Zend Framework on the back end, it is possible, with a minimum of code, to build out an application with a much richer UI than would be possible with HTML alone.

Flex brings an ease of coding to the front-end development that has been missing up to this point. The Zend Framework is flexible enough so that we were easily able to build the back-end so that it could meet not only our needs for today but allow us to expand our front-end options easily.

Combining the strengths of these two power tools allows us as developers to concentrate more on what we are trying to build and less on how we have to build it. Or in my case, leaves some time in my schedule for art lessons.