Simple CakeAMFPhp How To

It’s been a while since I last wrote, sorry! I’ve been busy with work, thesis and some other suffs (which I’ll be posting about in the next days).
One of those is AMFPhp: a method to connect Flash with Php.
Why AMF instead of JSON or XML? Basically because AMF is more mature than JSON, offer a ready-made and really complete set of classes, many utilities like the connection debugger component, a great Php implementation, and, above all, CakeAMFPhp. (AMF is also a binary protocol on top of HTTP, so performance should be better than XML)
CakeAMFPhp is the integration of AMFPhp and Cake, a Rails-inspired php framework. With Cake I’ve quickly build a prototype with the Bake command-line utility. Then I’ve started implementing the methods for the AMF calls. Thanks to the great database abstraction, every method is only few lines long.

Since CakeAMFPhp is a beta, I’ve found some problems, for which I’ve found a quick fix that I’ll explain shortly.

To start a CakeAMFPhp project, simply download the required libraries as explained in this tutorial. (If you are new to Cake you should also see this)
Then in your controller, for example GalleriesController, put this:

vendor("cakeamfphp/amf-core/util/MethodTable");
vendor('cakeamfphp/amf-core/adapters/lib/Arrayft');
vendor('cakeamfphp/amf-core/adapters/lib/Arrayf');

class GalleriesController extends AppController
{
...
function GalleriesController()
{
  //AMF:
  $this->methodTable = MethodTable::create(__FILE__);
  parent::__construct();
}

In the line 11 we call the automatic method table constructor of AMFPhp: it read the javadoc-like comments in the file and do all the magic:

/**
* @desc Return the list of public galleries
* @access remote
* @pagesize 25
*/
function getGalleries($offset = 0, $limit = 25) {
  $data = $this->Gallery->findAll(null, null, 'created DESC', $limit, $offset, 0);
  return $this->_arrayft($data, 'Gallery');
}

function getGalleries_count() {
  return $this->Gallery->findCount();
}

The @access remote tag specify that the getGalleries method is callable from flash:

[as] var ser = new Service(“http://localhost/cake_gateway.php”, null, ‘GalleriesController’, null , null);
var pc:PendingCall = ser.getGalleries();
pc.responder = new RelayResponder(this, “handleResult”, “handleError”);
[/as]

The getGalleries_count() method is needed for the pageable recordset:

[as] function handleResult (re:ResultEvent) {
var rs = RecordSet(re.result);
rs.addEventListener(‘modelChanged’, this); //Listen for updateItems
this.totalItems = rs.getLength();

for(var i:Number=startFrom; i < l; i++) { item = rs.getItemAt(i); if(i < rs.getNumberAvailable()) { //It is already fetched ... //Process a row } } [/as] getGalleries return only the first 25 row from the database. When the data is loaded in flash, rs.getLength() method return the value from getGalleries_count(). getItemAt return an item or a future, we check if the item is loaded in the line 8. If the item isn’t from the ones loaded, it is automatically requested from the server when we call getItemAt. When it arrives, the modelChanged method is called:

[as] function modelChanged (info:Object) {
var rs = this.rs;
var item:Object;
if(info.eventName == “updateItems”) {
for(var i:Number=info.firstItem; i <= info.lastItem; i++) { item = rs.getItemAt(i); target.addItem(item); } } } [/as] in the info object there are the rows received, you can configure Flash to get only the item required or the page containing it. That's all, simply add the @access remote tag, a *methodname*_count method and it works.
You can find more information on pageable recordsets here.

The _arrayft is required for the issue I’ve talk about before: CakeAMFPhp 0.6 does not support sending recorset to flash, so this method is required to transform the data returned by the Cake database abstraction layer into a AMF-compatible recordset:

function _arrayft($data, $table) {
  $r = array();
  if(!empty($data[$table])) {
    //Multi table query:
    if(is_array($data[$table][0])) {
      //Nested table:
      foreach($data[$table] as $t) {
        $r[] = $t;
      }
    }
    else {
    //Sigle record:
      foreach($data[$table] as $t) {
        $r[] = $t;
      }
    }
  }
  else {
    foreach($data as $t) {
      array_push($r, $t[$table]);
    }
  }
  return new Arrayf($r, array_keys($r[0])  );
}

For performance reason, it is suggested to use a 0 ‘recursive’ value in the find(..) call.

2 thoughts on “Simple CakeAMFPhp How To

  1. Brian says:

    Okay, now I’m not sure how well this will perform but it appears to work so far. The following is a modified version of the amfBB example.

    	function amfRead($offset = 0, $limit = 10) 
    	{
    		$data = $this->BulletinBoard->findAll(null, null, 'BulletinBoard.created DESC', $limit, $page, 0);
    		
    		$r = array();
    		foreach(array_keys($data[0]) as $tableName)
    		{
    			for($i=0;$i$value)
    				{
    					$r[$i][$tableName.'_'.$key] = $value;
    				}
    			}
    		}
    		
    		return new Arrayf($r, array_keys($r[0]) );
    	}
    	
    	function amfRead_count() {
    		return $this->BulletinBoard->findCount();
    	}
    

    Which will return the following:

    RecordSet 
    -data
    --array 
    ---0
    ----array 
    -----BulletinBoard_id		1 
    -----BulletinBoard_user_id	1 
    -----BulletinBoard_email	user@domain.com 
    -----BulletinBoard_url		http://www.domain.com 
    -----BulletinBoard_message	Some message
    -----BulletinBoard_created	2007-02-15 11:33:26 
    -----BulletinBoard_modified	2007-02-15 11:33:26 
    -----User_id		1 
    -----User_name		user 
    -----User_created		2007-02-14 15:19:52 
    -----User_modified		2007-02-14 15:19:52 
    

    – Brian

Comments are closed.