Prerequisite knowledge
Knowledge of PHP, ActionScript, MXML, and SQL are required to complete the steps in this tutorial..
Additional required products:
User level
Flash Builder 4 (Download trial)
In May 2010, I wrote a blog post that explored how to create a simple application that used the Doctrine ORM (Object Relational Mapper) framework for PHP on the server side, Flex on the client side, and remoting via the Zend Framework to communicate between Flex and PHP.
At that time my feelings on Doctrine were mixed. I chatted with Jonathan Wage of Doctrine about some of the shortcomings I found in Doctrine 1.x and he suggested that I check out Doctrine 2 Beta. I've found that Doctrine 2 is a big step forward.
In this article I describe how I rewrote the application that I created for my original blog post, this time using Doctrine 2, Flex 4, the Zend Framework, and the Flash Builder data-centric development features. I'll highlight the relevant differences between Doctrine 1 and Doctrine 2 along the way. Thus, you should find this article valuable in any one of these two cases:
  • You are already working with Doctrine 1 and you've wondered what it would take to move to Doctrine 2
  • You want to learn how to use Doctrine 2 with Flex; you know PHP and you know enough Flex not to be scared away if you see some snippets of code

The big picture

If you aren't already using an ORM framework for PHP, then I strongly encourage you to consider it. For most projects, it frees you of the tedious task of writing Create, Read, Update, and Delete (CRUD) code and SQL queries. It allows you to focus on the business logic of your application. Better yet, these advantages are multiplied when working on Rich Internet Applications because for these applications, much of the work is done on the client and not on the server.
There are some aspects of using ORM with RIA that could be better. For example, when you use a server side ORM to feed data to a rich client (and enable the client to persist changes), you need additional boiler plate code to make the whole thing work.
If you don't know much about ORMs in general, you may want to read my initial blog post on the topic, Working with Doctrine 1.x, Zend Framework, and Flex, before continuing.
The sample application for this article provides a simple interface for tracking student grades (or marks) in a series of courses (see Figure 1).
The database structure for this application comprises four tables: marks, courses, students, and countries (see Figure 2).
Note: The only difference between this database and the one used in my original blog post is the addition of a simple primary key to the marks table.
While this is by no means a complex application, it has reasonably sophisticated relationships between tables. There are several courses and countries (courses and countries tables), and each student belongs to a country (many-to-one between students and countries) and receives marks for a number of courses (marks many-to-many table for students and courses tables).
The Flex application reads all the data stored in the MySQL database and enables the user to fully edit student records by performing the following operations:
  • Add, edit, delete students
  • Change the country for a student
  • Change the courses taken by a student and assign marks for a course
The complete application includes the Flex client, the PHP application server with Zend AMF, and the MySQL database server (see Figure 3).

Differences between Doctrine 2 and Doctrine 1.x

If you're familiar with Doctrine 1.x, it will help to understand the main differences between Doctrine 2 and Doctrine 1.x.
Note: Doctrine 2 is still in Beta, so features may change before the final release.
The biggest change by far is related to the main pattern used by the Doctrine 2 ORM. Doctrine 1.x versions used the Active Record pattern (Ruby on Rails uses Active Record too); whereas Doctrine 2 uses the Data Mapper pattern (Hibernate uses the same pattern). With Active Record, the entities know how to persist themselves to the database. Basically each entity extends a class from the ORM framework and implements methods such as read() , find() , delete() , and update() . Although it is not mandatory, the entities look very much like the database structure.
With Data Mapper, the entities know nothing about the persistence layer and nothing about the database structure. The ORM provides the means to persist the entities and read the data into entities (from the database).
From a rich client perspective, this translates into less work on the PHP side when preparing data to send across the wire when using Doctrine 2. With Doctrine 1, the data model was heavy due to the Active Record pattern. When I worked with Doctrine 1.x, I had to create a plain vanilla data model to send the data to the Flex client. In order to efficiently transform the heavy entities used by Doctrine 1.x into the plain vanilla ones used for sending the data to Flex, I had to write custom functions. When data came in from the Flex client, the reverse process was needed; I used the plain vanilla objects to build the Doctrine entity objects. An alternative could be to send all the data to Flex as arrays. Unfortunately, this approach doesn't work out of the box; you have to write functions to transform the graph of objects into a graph of arrays or associative arrays.
With Doctrine 2, you don't need this extra layer of simple value objects and you can return the data as a graph of objects or arrays with the built-in capabilities.
The second big difference is that Doctrine 2 requires PHP 5.3 or newer. Thus, if your setup requires older versions of PHP, then you have to stay with Doctrine 1.x.
Of course the ripples stirred by these two changes are quite big and I think it is safe to say that when moving from Doctrine 1.x to Doctrine 2 you won't reuse much of your previous experience with Doctrine 1.x or the code you wrote.
Having said this, I have to say that I am happy with the evolution of Doctrine, because I favor Data Mapper over Active Record.

Getting the source code

This article outlines the main steps in building the sample application. The sample files included with this article include the PHP doctrine2_students folder, the database SQL dump for creating the tables, and the Flex code.
The easiest way to get started with this project is just to extract the ZIP file and import the project in Flash Builder. Next, place the PHP code (the doctrine2_students folder) in your web root folder. Get the Doctrine 2 files, and then reconfigure the doctrine2_students/bootstrap.php file to define the Doctrine 2 location and update your database credentials. Finally, you can explore the sample Flex code or create your own new Flex/PHP project and perform the steps in the following sections, which include creating the Flex wrapper services and value objects.

Installing Doctrine 2 and creating the PHP project

There are four different ways (PEAR, Package Download, GitHub, or SVN) to get the Doctrine framework. The configuration of Doctrine will differ depending on what method you use to obtain it. I used GitHub for my project, and I pulled the code outside of the Apache web root.
I recommend using Eclipse PDT with Flash Builder 4 for this kind of development. Follow these steps to use this setup:
  1. Install Eclipse PDT and then install the Flash Builder plug-in on top of Eclipse PDT.
  2. Create a PHP project named students-doctrine2.
  3. Add Flex nature to the project by right-clicking on the project name and selecting Add/Change Project Type > Add Flex Project Type. Make sure you select PHP for the Application Server type and you fill in the path and URL for your web root.
  4. After downloading Doctrine, create a folder named doctrine2_students inside the web root for the PHP services, entities, and Doctrine configuration files.
  5. Inside that folder, create three subfolders named entities, proxies, and services.
  6. Create a linked resource between the doctrine2_students folder and the Eclipse project. (Right-click on the project, choose New > Folder, click Advanced, and navigate to the folder location.)
Now you're ready to write PHP and Flex code.
The next step is to create a bootstrap file (I named mine bootstrap.php and placed it inside of the doctrine2_students folder) that configures Doctrine 2 for your project. This file is used to load the framework classes, set up database access information, specify the location and annotation method of entities, and configure the different caches that will be used by the application. In the same bootstrap file you can create an instance of the EntityManager class. This is the entry point to Doctrine 2. In the sample files for this article you'll find the bootstrap.php file in the doctrine2_students folder. The file looks like this:
<?php use Doctrine\ORM\EntityManager, Doctrine\ORM\Configuration; $applicationMode = 'development'; //Doctrine Git bootstrap $lib = '/Users/mcorlan/Documents/work/_git/doctrine/doctrine2/lib/'; require $lib . 'vendor/doctrine-common/lib/Doctrine/Common/ClassLoader.php'; $classLoader = new \Doctrine\Common\ClassLoader('Doctrine\Common', $lib . 'vendor/doctrine-common/lib'); $classLoader->register(); $classLoader = new \Doctrine\Common\ClassLoader('Doctrine\DBAL', $lib . 'vendor/doctrine-dbal/lib'); $classLoader->register(); $classLoader = new \Doctrine\Common\ClassLoader('Doctrine\ORM', $lib); $classLoader->register(); //additional Symphony components for Doctrine-CLI Tool, YAML Mapping driver $classloader = new \Doctrine\Common\ClassLoader('Symfony', $lib . 'vendor/'); $classloader->register(); //load entities $classloader = new \Doctrine\Common\ClassLoader('entities', __DIR__); $classloader->register(); $classLoader = new \Doctrine\Common\ClassLoader('proxies', __DIR__); $classLoader->register(); //load services $classLoader = new \Doctrine\Common\ClassLoader(null, __DIR__ . '/services'); $classLoader->register(); if ($applicationMode == 'development') { $cache = new \Doctrine\Common\Cache\ArrayCache; } else { $cache = new \Doctrine\Common\Cache\XcacheCache(); } $config = new Configuration; $config->setMetadataCacheImpl($cache); $driverImpl = $config->newDefaultAnnotationDriver(__DIR__ . '/entities'); $config->setMetadataDriverImpl($driverImpl); $config->setQueryCacheImpl($cache); $config->setProxyDir(dirname(__FILE__) .'/proxies'); $config->setProxyNamespace('doctrine2_students\proxies'); if ($applicationMode == "development") { $config->setAutoGenerateProxyClasses(true); } else { $config->setAutoGenerateProxyClasses(false); } //database connection config $connectionOptions = array( 'driver' => 'pdo_mysql', 'dbname' => 'students', 'user' => 'mihai', 'password' => 'mihai' ); $GLOBALS['em'] = EntityManager::create($connectionOptions, $config);
If you use this file, remember to change the Doctrine paths as well as the MySQL username and password to match your own setup.

Creating the PHP entities

With Doctrine 2 (and any ORM that uses the Data Mapper pattern) you have to specify how an entity is persisted by the framework and what relationships it has with other entities (if any). In Doctrine 2 you can choose from four different mechanisms: annotations, YAML, XML, and plain PHP. I initially favored annotations because all the information is stored in the entities classes as PHPDoc comments. Thus if you want to modify an entity you have only one place to look for it. However, after using this approach I think the XML approach is best because you get code-completion hints. Here is the listing for the Course entity (remember there are four tables in the database and so the application needs four entities):
<?php namespace entities; /** @Entity * @Table(name="courses") */ class Course { /** * @Id @Column(type="integer") * @GeneratedValue(strategy="AUTO") */ private $id; /** * @Column(type="string", length=255) */ private $name; public function getId() { return $this->id; } public function getName() { return $this->name; } public function setName($val) { $this->name = $val; } }
See the entities folder in the sample files for the Country, Mark, and Student entities.
You use annotations to specify what table is used for the entity (remember, one row from that table will be wrapped in one instance of the entity). You can set different names for the properties if you want (in SQL you don't use camelCase notation, but in PHP or ActionScript typically you use this convention).
The entities I created closely follow the structure of the database. The only difference is in how the foreign keys are represented. For example, the Student entity, which has a many-to-one relation with the Country entity, doesn't have a country_id property of type int . Instead, I added a property named country that is of type Country . Similarly, I created a property named marks that holds an array of Mark entities. For example, the marks property of a student who attends three courses will hold an array of Mark objects with three instances.
  • When I worked with Doctrine 1.x, I used the built-in tools to create the domain model from the database structure. With Doctrine 2, you can create the YAML out of the database schema and then generate the entities. I'm not convinced, however, that it is good idea to do this. If you have complex schemas the generated code might not worked as you expect, and you'll need to tweak it manually anyway. In any case, you have to remember to set the properties as private or protected and not public , and make sure you add getters and setters. If you don't, you likely encounter some nasty bugs because Doctrine will have problems injecting the code to handle relations.

Creating the PHP services

With the four entities in place, it is time to create the PHP services, which will be used by the Flex client to get and persist data. Basically, you'll use the Zend Framework to invoke remote procedure calls on these objects from the Flex application.
Inside the doctrine2_students/services/ folder you'll find four PHP files: CountriesService.php, CoursesService.php, MarksService.php, and StudentsService.php. If you examine the bootstrap.php file, you'll see that it loads the services folder along with the rest of the files. Here is the listing for the CountriesService class:
<?php require_once(__DIR__.'/../bootstrap.php'); class CountriesService { public function __construct() { $this->entityManager = $GLOBALS['em']; } public function getCountries() { $q = $this->entityManager->createQuery('SELECT c FROM entities\Country c ORDER BY'); return $q->getArrayResult(); } }
As you might expect, the complex code is inside the StudentsService class; here's the public API:
class StudentsService { //returns all the students public function getStudents() { ... } //save changes for an existent student, or insert a new one public function saveStudent($student) { ... } //deletes a student public function deleteStudent($student) { ... } }

Using Doctrine Query Language

You can use the entry point to Doctrine 2—the EntityManager class—to query for persistent objects using a few different methods. The most powerful method is Doctrine Query Language (DQL), which resembles SQL but works on the entities you've defined in your domain model rather than on the underlying tables.
If, for example, you want to retrieve the country with the id equal to 1 , you could use this code:
$id = 1; $dql = 'SELECT c FROM entities\Country c WHERE id = ?1'; $query = $entityManager->createQuery($dql); $query->setParameter(1, $id); $countryEntity = $query->getResult();
If you want to change the name for this country, you could use the following code:
$countryEntity->setName('new name for your country'); //persist the changes to database $entityManager->flush();
If you want to create a new country, you’d write this code:
$countryEntity = new entities\Country(); $countryEntity->setName('Mihai\'s country'); //set the entity to be managed by Doctrine $entityManager->persist($countryEntity); //persist the changes to database $entityManager->flush();
With DQL when you write a join, it can be a filtering join (similar to the concept of join in SQL used for limiting or aggregating results) or a fetch join (used to fetch related records and include them in the result of the main query). When you include fields from the joined entity in the SELECT clause you get a fetch join. Here is the code for the StudentsService->getStudents() method:
StudentsService->getStudents() method: $dql = "SELECT s, c, m, e FROM entities\Student s JOIN c JOIN s.marks m JOIN m.course e ORDER BY s.lastName, s.firstName"; $q = $this->entityManager->createQuery($dql); return $q->getArrayResult();
Each student is retrieved along with the country he belongs to and all of his courses from the many-to-many table—all with a single DQL query. If you use print_r() to show the results you'll see entire structure (see Figure 4).
When you create a query with a fetch join and you use the getArrayResult() method on the query object instead of getResult() , you get an array or associative array of other arrays. These data are ready to be sent to the Flex client without any transformation.
Note: Handling updates to a Student is covered in the next section, Creating the Flex client.
The sample application uses remoting—enabled by Zend AMF—to communicate between the Flex client and the PHP side. In the next section you'll also see how to use Flash Builder 4 to set up the Zend Framework and create the gateway for exposing the four PHP services.

Creating the Flex client

With the server code in place, it is time to add the Flex code. When I developed the Doctrine 1.x version of the application, I wrote all client code manually. For this article, you'll use the data-centric development features of Flash Builder to introspect PHP classes and create the service wrappers as well as the ActionScript value objects.
The easiest way to do this is to first create the four services, and then to define the return types for the get…() operations. Follow these steps:
  1. From the Data/Services view click the Connect to Data/Service link or the Connect to Data/Service button (it's the third button in the toolbar and it has a plus sign in its icon).
  2. When the wizard opens, select PHP, and click Next.
  3. If this is your first time using the wizard for PHP, then you'll be given the opportunity to install the Zend Framework. Follow the instructions to install it.
  4. Click Browse and select the first PHP service.
  5. If you want to, you can change the package in which the services (and the value objects) will be created.
  6. Click Finish.
  7. Repeat these steps for each of the other three services (see Figure 5).
Now, you're ready to define the value objects used on the Flex side. Again you can use the data-centric development features. Because the StudentsService returns a complex type that uses Student, Course, Country, and Mark it is important to start defining those return types first. Start with CountriesService and CoursesService, and then move on to MarksService, before defining the StudentsService value object.
  1. To define the return type for an operation, expand the tree for the service, right-click the operation (for example, select getCountries() from CountriesService) and choose Configure Return Type.
  2. When the wizard opens, select Auto-detect The Return Type From Sample Data and click Next.
  3. After Flash Builder introspects the service, you can type a name for the value object class; for example, type Country.
  4. The most complex type is the return type for the StudentsService.getStudents() method. For this one, on the second page of the wizard you need to expand the nodes and choose the types you defined earlier (Course, Country, or Mark) for the type column (see Figure 6).
With the service wrappers and value objects in place, it is time to take care of the application UI and put these files to use.
When the application starts, it needs to load the courses, countries, and students first. This is done in the init() function, which is registered on the creationComplete event of the application.
After you create the init() function, select the getStudents() method from the Data/Services view, right-click it, and choose Generate Service Call. This command adds the following to the code:
an instance of StudentsService
an instance of CallResponder (you use this object to retrieve the result via the lastResult property or to register a result/fault listener for that operation)
a method that makes the call to the selected operation and assigns the token returned by the operation to the token property of the CallResponder object
For example, here is the code generated for getStudents():
private function getStudents():void { getStudentsResult.token = studentsService.getStudents(); } … <s:CallResponder id="getStudentsResult"/> <services:StudentsService id="studentsService" fault=" + '\n' + event.fault.faultDetail)" showBusyCursor="true"/>
Take a look at the complete code in Main.mxml to see how it all fits together.
In the code you'll see that bidirectional binding is used for the firstName , lastName , and registration fields of the form:
<mx:FormItem label="First:"> <s:TextInput id="first" text="@{student.firstName}"/> </mx:FormItem> <mx:FormItem label="Last:"> <s:TextInput id="last" text="@{student.lastName}"/> </mx:FormItem> <mx:FormItem label="Registration:"> <mx:DateField id="registration" selectedDate="@{student.registration}"/> </mx:FormItem>
When the saveStudent() method is called it invokes the remote operation ( saveStudent() from the StudentsService.php) and passes along an instance of the Student ActionScript class. The PHP method ( StudentsService->saveStudent() ) receives an anonymous Object, so it has to manually build an instance of the Student entity and populate it with the data. Here is the complete code for the server-side saveStudent() method:
public function saveStudent($student) { if ($student->id) { //update $entity = $this->entityManager->find('entities\Student', $student->id); if (!$entity) throw new Exception('Error saving student!'); $marks = $entity->getMarks(); foreach ($marks as $mark) { //update mark value for existent records $found = false; foreach ($student->marks as $record) { if ($mark->getCourse()->getId() == $record->course->id) { $mark->setMark($record->mark); $found = true; $key = array_search($record, $student->marks, true); //remove the $record from array if ($key !== false) unset($student->marks[$key]); break; } } if (!$found) { //remove current mark $entity->removeMark($mark); $this->entityManager->remove($mark);//remove mark from database } } } else { //insert $entity = new entities\Student(); $this->entityManager->persist($entity); } $this->addNewMarks($entity, $student); //add new marks if any $entity->setFirstName($student->firstName); $entity->setLastName($student->lastName); $d = new DateTime(); $d->setTimestamp($student->registration->getTimestamp()); $entity->setRegistration($d); $country = $this->entityManager->find('entities\Country', $student->country->id); if (!$country) throw new Exception('Error saving student; invalid country!'); $entity->setCountry($country); $this->entityManager->flush(); //save the student }
If you're think this is way too much code for this "simple" operation, I'm mostly in agreement with you. However, it's important to remember that this code handles many tasks: creating a new student, inserting marks in the Marks2 table, updating a student, and updating marks if they were changed.
In contrast the deleteStudent method is quite clean (remember that behind the scenes it removes all the related records from the marks many-to-many table):
public function deleteStudent($student) { $entity = $this->entityManager->find('entities\Student', $student->id); if (!$entity) throw new Exception('Error deleting student!'); $this->entityManager->remove($entity); $this->entityManager->flush(); }

Doctrine 2 advantages (and some things to watch out for)

It's important to keep in mind that Doctrine 2 is still in beta, so conclusions drawn from working with it now are subject to change once the final version is released.
From what I've seen, though, Doctrine 2 makes it easier to work on PHP and Flex projects. I especially love the new Data Mapper approach and the flexibility and power it provides. The entities are very light and you can easily use DQL in conjunction with getArrayResult() to build a data structure that's ready to be sent to Flex. There is no need for all the plumbing work I did for my Doctrine 1 project to send the objects on the PHP side.
With Doctrine 2 you get a big productivity boost in terms of writing the PHP services and exposing the data to the Flex client. And if you think about it, the server side is not the place where most of the effort goes. So it is a good thing to have a framework that standardizes the PHP code and helps you retrieve and persist data. That said, however, you can tell that it is not a framework architected with rich clients in mind. (There is nothing particularly bad about this; the same is true of most frameworks out there). While it is easy to retrieve data from the underlying persistence layer and send them to a rich client, it is relatively difficult to persist the changes made in the client. On the server side, you have to write custom code to create the PHP entities out of the data received from the client before you can persist the changes. What I feel it is missing is a way to use a data structure (for example an associative array) as the source for creating the PHP entities (more on this below).
Another interesting departure from the Doctrine 1 project is that I didn't create an exact match between the ActionScript and PHP entities. When I designed the two sides of the equation, I had in mind the best domain model to serve the Flex client because all the information is edited on the client. Then I used the getArrayResult() method to retrieve associative arrays, which I sent to Flex where they are deserialized into objects.
One feature I stayed away from instinctively (both with Doctrine 1 and Doctrine 2) was the ability to generate the database schema using the entities and the mapping between them. With this feature, you can start your project by first writing the PHP data model, and then use Doctrine to generate the database for you. I'm old school and I've learned that relational databases treat you well if you treat them well. Thus, I opted to create the database myself to make sure I set all the indexes and constraints that I needed. I'm not saying that there are any problems with Doctrine feature for generating database schema; I simply haven't tried it.
Using Doctrine 2, you have very little work to do when handling the Delete and Read CRUD operations on the server side. However, Create and Update are a different story, especially when the object has associations (many-to-many, one-to-many, or many-to-one) with other entities. I thought it would be enough to retrieve the existing Student data with Doctrine and call the removeMark() method to remove a Mark. In fact, doing this doesn't delete the entry from the many-to-many table. Instead, you have to explicitly remove the Mark instance from the Student and from the entityManager ; for example:
$student->removeMark($mark); $this->entityManager->remove($mark);
Doctrine 2 offers a Cascade feature for persist and remove. For example, here is how you define a cascade delete/update for the Marks entities on the Student object:
class Student { ... /** * @OneToMany(targetEntity="Mark", mappedBy="student", cascade={"persist", "remove"}) */ private $marks; ... }
However, I found that this actually works perfectly only on delete. It is possible that I didn't fully understand the usage, I was expecting more than intended, or I just missed something.
Another small glitch was related to the composite primary keys. When I tried to follow Doctrine's documentation and annotate the Mark entity to compose the primary key out of student_id and course_id , I got a runtime error. As a workaround, I altered the table and added an auto-increment primary key.
The only other thing I didn't like was the date handling. When you send a Date object to the PHP side, the PHP code gets a Zend_Date object (when using the Zend Framework). Because Doctrine 2 doesn't know how to handle this kind of object (it uses the PHP DateTime object), you have to handle the transformation manually. It would be helpful to either configure the Zend Framework to use the PHP DateTime instead of Zend_Date or to have Doctrine 2 handle this format.

Where to go from here

I encourage you to read the excellent documentation you'll find on the Doctrine website to better understand the inner workings of Doctrine 2, the different types of associations it supports, and in general the features it offers.
The data-centric development features of Flash Builder greatly simplified the creation of the sample application's data model. They work for many server side technologies, and in this case they really saved a lot of time. For more information on that topic, see Ryan Stewart's three-part series of articles, starting with Flash Builder 4 and PHP – Part 1: Data-centric development. Also see Flex and PHP in the Adobe Developer Connection's Flex Developer Center for other tutorials on connecting a Flex application to a PHP back end.