For the Flash developers – building your remote endpoint

3 Comments

While I am not a great Flash developer… OK I'm not even a good one, I have been recently been looking at ways of integrating Flash and PHP with the release of Flash Builder 4.  I think that PHP is a great way for Flash developers to access their backend systems.  This is not because I have anything against any other technologies that a Flash developer might connect with, but instead it is because PHP is a great language to use when another language is your forte.

What I mean by that is that if Flash is your primary programming language PHP is a good way to provide remote access to your data because it doesn't require you to be an expert.  You can throw PHP at the wall and it will stick.  So what we're going to do in this article is show how little of an expert you really need to be.

One of the ways you can get away without being an expert is to use a framework.  In this case, I will be using Zend Framework as the means to provide my data access layer.  In fact, this example is a great one because it shows the use-at-will architecture of Zend Framework.  I believe that this is one of the places where Zend Framework rises above the others.  If you're looking for stupid simple database access and basic application functionality then Zend Framework might not be right for you.  But if you want the ability to use the components you need to make your life easier, ZF is a good choice.

This article is going to end up being a two part article.  This first part is intended for the Flash developer who wants to be able to set up a basic CRUD application.  The CRUD application is one that Ryan Stewart or Kevin Hoyt (or someone else) wrote that was designed to showcase connecting to PHP with Flash.  However, the example was written from the point of view of a Flash developer and what I wanted to do was take that same functionality and "Framework-ize" it.  In other words, make it very testable and drastically simplify the code.  The application is a simple one that provides access to census data.

LayoutThe new backend will use Zend_Application for its bootstrapping and setup.  Thankfully, if you are using Zend Studio, you can quite easily set this project up.  Select the PHP Explorer and type CTRL-2 followed by "zf create project CService".  This will create a basic structure of an MVC application, though we will only be using the M (or Model) part of the MVC design pattern.  So, we're using the M pattern.  If you do not have Zend Studio you can do the same thing with Zend_Tool (wha?) or you could simply create a project structure that looks like what is on the right(oh).

I had mentioned bootstrapping.  What that does is set up the context for the request so you don't have to do it.  You see in the "application" directory a Bootstrap.php file.  This contains the class that Zend_Application will use to bootstrap a request and it looks like this.


class Bootstrap extends Zend_Application_Bootstrap_Bootstrap
{
    public function _initAutoload()
    {
        $al = new Zend_Application_Module_Autoloader(
            array(
                'namespace'        => '',
                'basePath'        => dirname(__FILE__)
            )
        );
    }
}

What this is doing is setting up the autoloader.  The autoloader is used to load files for individual classes that are required to execute an individual request.  This can get more complex, but likely you, as a Flash developer, will not need to run into the need for that.  Then in our configs directory we will need to spell out some basic database configuration settings.  I put it in a file called configs/application.ini.

[production]
bootstrap.path = APPLICATION_PATH "/Bootstrap.php"
resources.db.adapter = "pdo_mysql"
resources.db.params.host = "localhost"
resources.db.params.username = "root"
resources.db.params.password = ""
resources.db.params.dbname = "census"
resources.db.isDefaultTableAdapter = true

ModelsThe next thing we need to do is set up our data access layer.  This is not the layer that you will be calling.  This is the layer that the service layer will call to handle the database calls.  There are two files that you need to create.  One to represent the table and another to represent a row.  The one two represent the table is in the models/DbTable/Census.php file and its code looks like this.


class Model_DbTable_Census extends Zend_Db_Table_Abstract
{
    protected $_rowClass = 'Model_Census';
    protected $_name = 'census';
}

$_name is the name of the table as spelled in the database.  $_rowClass is the name of the class that represents the row.  Speaking of, that file is stored in models/Census.php and it looks like this.


class Model_Census extends Zend_Db_Table_Row_Abstract
{
    protected $_primary = 'id'; 
}

What this is doing is stating that the primary key is the column named "id".  Speaking of the table.  Here is the create statement.

CREATE TABLE `census` (
  `age` varchar(3) DEFAULT NULL,
  `classofworker` varchar(255) DEFAULT NULL,
  `education` varchar(255) DEFAULT NULL,
  `maritalstatus` varchar(255) DEFAULT NULL,
  `race` varchar(255) DEFAULT NULL,
  `sex` varchar(255) DEFAULT NULL,
  `id` int(11) NOT NULL AUTO_INCREMENT,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf-8

Congratulations, you now have you model layer.

ServicesThe next layer you need to build is the service access layer.  This is done after you have created your PHP data service using the Flash Builder components.  There are plenty of places you can go to on information on that, or you could also look at my previous posting on Flash called Flex and Zend Framework – Part 1.  Once you have created the data access integration point in Flash Builder we can go back to PHP.  If you look at our first image you see that there is a directory called "public".  This is the directory that the service endpoint should be installed into.  In that service endpoint directory there is a file called amf_config.ini.  Here is where you need to set up the directory where your service class is going to be.  I simply named the directory "services".  The gateway.php file was put there by Flash Builder but the bootstrap.php was not.  We will look at bootstrap.php in just a bit.

The next thing we want to do is define our service class.  This is the class that will implement the functionality that we want to actually call.  It is placed in the "services" directory in a file called CensusService.php.  I won't put the whole class in, but from the example you should see what it's doing.


class CensusService
{

    public function getAllCensus()
    {
        $tbl = new Model_DbTable_Census();
        return $tbl->fetchAll()->toArray();
    }

    public function getCensusByID( $itemID )
    {    
        $tbl = new Model_DbTable_Census();
        return $tbl->find($itemID)->current();
    }

    public function createCensus( $item )
    {
        $tbl = new Model_DbTable_Census();
        $row = $tbl->fetchNew();
        $row->setFromArray((array)$item);
        $row->save();
        return $row->id;
    }
}

Notice the very few lines of code?  This is the benefit of using something like Zend Framework.  No database access or anything.  That has all been taken care of by Zend_Application and the configuration file that we wrote.  The only caveat is that the return value MUST be an array.  So we put toArray() at the end.

Well it's all working now, right?  Maybe.  To do that we need to now test it.  That is where Unit Testing comes in.  Using Unit Testing on the PHP side allows us to test to make sure that any changes made to the codebase don't break somewhere else.  To do the Unit Testing I am going to use PHPUnit integration in Zend Studio.  You could also use the regular PHPUnit if you like.

Remember that "tests" directory that we had created earlier on?  Well, in there you can create another directory called "services" and in that directory put a file called CensusServiceTest.php.  In that file is where you write all of your Unit Tests.  I have a video you can watch called "Unit Testing Zend Server PHP Job Queues" that goes over some of the basics.  The Unit Tests (with several of the tests removed for brevity) start with including the files to be tested, setting up the test and then calling the individual functions, comparing the results to what is expected.

require_once realpath(dirname(__FILE__).'/../application/bootstrap.php');
require_once realpath(dirname(__FILE__).'/../../public/AMFService/services/CensusService.php');

/**
 * CensusService test case.
 */
class CensusServiceTest extends PHPUnit_Framework_TestCase {
    
    /**
     * @var CensusService
     */
    private $CensusService;
    
    protected function setUp() {
        parent::setUp ();

        $this->CensusService = new CensusService();
    
    }
    
    /**
     * Tests CensusService->getAllCensus()
     */
    public function testGetAllCensus() {
       
        $ret = $this->CensusService->getAllCensus();
        $this->assertGreaterThan(
            0,
            count(
                $ret
            )
        );
        $this->assertType(
            'array',
            $ret
        );
    
    }
}

What we are doing is asserting that this code will return more than one census item.  Now for each piece of functionality you should add another function that starts with the word "test" and verify that the return values are what you would expect.  For our test on collections of information we want to assert both that the count is greater than zero and that the return type is an array, not a result set.

There is now one last thing we need to do before going on to the next part and integrating our front end Flash application.  That is to bootstrap the remoting requests.  That is done by adding a require call to the top of Flash Builder's gateway.php file.

require_once 'bootstrap.php';

ini_set("display_errors", 1);
// ..etc.

Then we need to actually create the file that does the bootstrapping.


// Define path to application directory
defined('APPLICATION_PATH')
    || define('APPLICATION_PATH', realpath(dirname(__FILE__) . '/../../application'));

// Define application environment
defined('APPLICATION_ENV')
    || define('APPLICATION_ENV', (getenv('APPLICATION_ENV') ? getenv('APPLICATION_ENV') : 'production'));

// Ensure library/ is on include_path
set_include_path(implode(PATH_SEPARATOR, array(
    realpath(APPLICATION_PATH . '/../../library'),
    get_include_path(),
)));

/** Zend_Application */
require_once 'Zend/Application.php';

// Create application, bootstrap, and run
$application = new Zend_Application(
    APPLICATION_ENV,
    APPLICATION_PATH . '/configs/application.ini'
);
$application->bootstrap();

Most of what you see here is mostly not important as a Flash developer.  What this is basically doing is setting the application path directory, defining the environment as production, setting the include path to include a library path (which you could probably remove) and then loading and executing the Zend_Application class with the current configuration.  All this is really doing is setting up those database models that we had defined earlier on.  While for a simple example this is a fair amount of overkill, it actually makes your life much easier as you start to add database tables and service layers.

That takes us to the end of the backend part of building a CRUD Flash application with PHP.  Stay tuned for when we integrate the frontend.

The Zend Studio projects can be downloaded here.

3 thoughts on “For the Flash developers – building your remote endpoint

  1. Hey man, quick question I’m hoping you can help with.. so I clearly have some sort of configuration issue. When I try to add the PHP data service to my Flex project, basically copying your examples above to get me started, I get:

    InvocationTargetException:There was an error while invoking the operation. Check your operation inputs or server code and try invoking the operation again.

    Error: Reason:
    Fatal error: Class ‘Model_DbTable_Users’ not found in G:wwwPhpProjectpublicservicesUserService.php on line 7

    Line 7 is, not shockingly: $tbl = new Model_DbTable_Users();

    What am I doing wrong in this config? Also, there seems to be a problem with the web/url roots of my Flex app, here’s a screenshot of my directory structure:

    http://dl.dropbox.com/u/163269/zend_flex_proj_outline.PNG

    Anyway, would really appreciate some help, can’t really get this project started until I have these config issues resolved!

  2. Matt, did you put the bootstrap.php reference at the top of the gateway.php file? The reason you’re getting that error is because the autoloader, defined in the Bootstrap class, has not be instantiated.

Leave a Reply

Your email address will not be published. Required fields are marked *