Ryan Stewart
2 August 2010
Prerequisite knowledge
Some experience developing Flex applications for PHP back-ends will be helpful.
User level
Required products
Flash Builder 4 (Download trial)
Sample files
One area in which Flex and PHP go really well together is in creating data visualizations. PHP makes it incredibly easy to retrieve information from a database and manipulate it. For its part, Flex provides a user interface layer that is interactive and capable of quickly calculating shapes, which is perfect for charting.
One of the main issues that you’ll encounter when using PHP and Flex together is how to best structure the data on the PHP side in order to bring it into the Flex application. Normally, you retrieve a set of data in a pseudo-random order, potentially ordered by id, and then plot that data. In a lot of cases, that works just fine. But for complex data types or for charts where you want to make it easy to interactively drill down into data, you have to use some tricks of the Flex framework to plot the data correctly.
In this article, you’ll see one way to organize the data in PHP and send it to the Flex client in a way that makes it simple to plot on a chart.
A look at the data
The example application for this article uses a dataset from peakbagger.com, which provides information on several high mountains from a few different states. Initially, the data in the chart will show the aggregate number of peaks per state with a specified minimum level of topographic prominence. The user can then click on the chart to see elevation and prominence data for all the peaks in that state. One of the benefits of Flex is that you can grab the data from the database up front and then change the chart without having to make another call to the database.
The structure of the data is an array of arrays with the subarray containing data about individual peaks. The database and object structure looks like this:
peaks | -------------------------------- id int peak_name varchar(200) elevation int prominence int county varchar(200) state varchar(2) range varchar(200) isolation float
When the data is returned to Flex, it is sent as an array of arrays that are grouped and sorted using the database abstraction classes of the Zend Framework. The code that is called directly from Flex looks like this:
class Peaks { public function getBarChartPeaks($maxStates, $minProminence) { $tbl = new Model_DbTable_Peak(); $select = $tbl->select() ->from('peaks', array('state', 'prominence',new Zend_Db_Expr('SUM(prominence) AS promTop'))) ->group('state') ->order('promTop DESC') ->limit($maxStates); $rs = $select->query(); $res = array(); foreach ($rs as $row) { $where = array('state = ?' => $row['state'], 'prominence >= ?' => $minProminence); $stateRes = $tbl->fetchAll($tbl->getAdapter()->quoteInto($where,""), 'prominence DESC'); $res[] = $stateRes->toArray(); } return $res; } }
This code relies on a structured Zend Framework project, which is included in the sample files for this article.
A basic column chart
The first step in the Flex application is to connect to the PHP service. I won't go into detail on how to do that here, but you can find more details in Flash Builder 4 and PHP – Part 1: Data-centric development and Flash Builder 4 and PHP – Part 2: Zend AMF and Flash Remoting.
Once you have your Flex applications set up to retrieve the data from your PHP back-end, start with a basic ColumnChart:
<mx:ColumnChart x="10" y="10" id="chart" dataProvider="{arrData}" type="clustered" showDataTips="true"> <mx:horizontalAxis> <mx:CategoryAxis id="catField" dataFunction="categoryFunction" /> </mx:horizontalAxis> <mx:series> <mx:ColumnSeries id="colSeries" dataFunction="dataFunction" /> <mx:ColumnSeries id="promSeries" yField="prominence" xField="name" visible="false" /> </mx:series> </mx:ColumnChart>
There are two parts to this column chart, the horizontal axis and then a ColumnSeries object. The ColumnSeries actually contains the data. The axis can be a linear axis (which would show sequential values), a date/time axis, or a logarithmic axis. To plot more arbitrary values–for instance a group–use the CategoryAxis tag. For this example, the data is state names, so it makes sense to use the CategoryAxis tag. There is just one piece of data to chart–the number of peaks in each state–so you just need one ColumnSeries to show it. The other, with the id set to promSeries, is used to show prominence later on after the user has interacted with it, so its visible property is set to false.
Normally, if the data is flat, you can use the xField and yField properties of the ColumnSeries component and set them to corresponding keys in the object. If you have a more complex dataset, such as an array of arrays, you need to manipulate the data before charting it. The dataFunction property can be set on both axes and series to do just that. It takes a function as a value, and that function returns the value that you want to appear on the chart. Create a function called categoryFunction for the CategoryAxis and a function called dataFunction for the ColumnSeries and put them in between the fx:Script tags:
<fx:Script> <![CDATA[ import mx.charts.chartClasses.AxisBase; import mx.charts.chartClasses.Series; protected function dataFunction(series:Series, item:Object, fieldName:String):Object { if(fieldName == "yValue") { return item.length; } else if(fieldName == "xValue") { return item[0].state; } else { return null; } } protected function categoryFunction(axis:AxisBase, item:Object):Object { return item[0].state; } ]]> </fx:Script>
The category function is pretty straightforward. It will be called as many times as there are arrays in the main array. It just pulls the state out and returns it as a category.
The function for ColumnSeries is a bit more complicated. Any series with values will have both x and y values and dataFunction needs to return both of those. It is called twice the number of times as there are arrays in the main array because it gets called for both the x and the y values. Using the fieldName property you can determine for which value it is being called. If it's the yValue then the function returns what you want to plot, namely the number of peaks in that array. If it's the xValue then you want to make sure it's associated with the category so you return the state.
Drilling down
If you run the application now, you'll see the count of peaks on the chart (see Figure 1).
Figure 1. The charting application’s initial display
Figure 1. The charting application’s initial display
Now you’re ready to allow users to drill down into the individual state and see the elevation and prominence of each peak. In Flex you can listen for events for specific user actions on the chart. In this case add an itemClick handler and function to the ColumnChart so that your ColumnChart tag looks like this:
<mx:ColumnChart x="10" y="10" id="chart" dataProvider="{arrData}" type="clustered" showDataTips="true" itemClick="chart_itemClickHandler(event)">
Then add the event handler in the script block:
import mx.charts.events.ChartItemEvent; protected function chart_itemClickHandler(event:ChartItemEvent):void { catField.categoryField = "name"; catField.dataFunction = null; colSeries.dataFunction = null; colSeries.yField = "elevation"; colSeries.xField = "name"; promSeries.visible = true; chart.dataProvider = event.hitData.item; }
Because the data structure is just an array of objects, you can null out the dataFunction properties on both the CategoryAxis and the ColumnSeries and replace them with the yField and xField values for the data you want to chart. In this case, you'll plot the elevation along the Y axis and show the name of the peak along the X axis.
Animating the transition
The last step is to add some animation so that when the user clicks on the chart, the old chart fades away and the new chart appears gracefully. To do that, you can use series effects. Add two SeriesSlide tags in between the fx:Declarations tags.
<mx:SeriesSlide id="slideIn" duration="1000" direction="up" /> <mx:SeriesSlide id="slideOut" duration="1000" direction="down" />

To apply the SeriesSlide objects to the chart, modify the ColumnSeries tags by adding the showDataEffect and hideDataEffect properties.
<mx:series> <mx:ColumnSeries id="colSeries" dataFunction="dataFunction" showDataEffect="slideIn" hideDataEffect="slideOut" /> <mx:ColumnSeries id="promSeries" yField="prominence" xField="name" visible="false" showDataEffect="slideIn" hideDataEffect="slideOut" /> </mx:series>
And that's all there is to it. Now you can run the application and see the final result.

Where to go from here

For more information on Flex and charting, see the following resources: