I finally got some time to play with Doctrine 2 and Flex. Back in May I wrote an article about working with Doctrine 1.x and Flex (you can read the article here) and my feelings were mixed. I chatted with Jonathan Wage of Doctrine about some of the shortcomings I found in Doctrine 1.x and his response was to check Doctrine 2 (still in development at the time of writing this article). Doctrine 2 is a big step forward.
In this article I describe how I rewrote the original application I created for my first article, this time using Doctrine 2, Flex 4, Zend Framework, and the Flash Builder data-centric development wizards. 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 version 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
Before going into the details let me say this: if you aren’t already using an ORM framework for PHP then you should. For most projects it can help you by freeing you of the tedious tasks of writing CRUD code and SQL queries. It allows you to focus on the business logic of your application. And all these advantages are multiplied when working on Rich Internet Applications because on this kind of project much of the work is done on the client and not on the server.
There are some aspects of using this ORM with RIA that could be better, but hey we don’t live in a perfect world. Most of these things, I think, are related to the fact that every time you use a server side ORM with a rich client, you leave behind the main story used for creating that framework – you use the ORM in order to feed a rich client with data and enable the client to persist the changes. Thus 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 first article first and before continuing.
The Big Picture
Here is a screenshot of the application I built running in the browser:
below is the database structure. The only difference between this database and the one used in the previous article is the addition of a simple primary key to the marks table.
While this is by no means a complex application, I think it’s a decent one in terms of relations between tables: I have a number of 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 lets the user fully edit students 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
And here is an overview of the different parties involved in this application:
Differences between Doctrine 2 and 1.x
Before explaining how I built the app, let’s talk a little bit about the main differences between Doctrine 2 and Doctrine 1.x (as I’m writing this article Doctrine 2 is still in Beta, so things could change).
The biggest change by far is the one related to the main pattern used by the Doctrine 2 ORM. In 1.x versions it used the Active Record pattern (Ruby on Rails uses Active Record too), now you can say goodbye to Active Record and welcome Data Mapper (Hibernate uses the same pattern). What are the differences between these two patterns?
With Active Record the entities know how to persist themselves to the database; basically each entity extends some sort of a class from the ORM framework and implements methods like read(), find(), delete(), and update(). Although it is not mandatory, the entities look very much like the database structure.
With Data Mapper entities know nothing about the persistency 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 in less work on the PHP side when preparing data for sending across the wire while using Doctrine 2. With Doctrine 1, the data model was heavy due to the Active Record pattern. Thus I had to create a plain vanilla data model used to send the data to the Flex client. In order to efficiently transform the heavy entities used by Doctrine 1 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: use 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/associative arrays.
With Doctrine 2, I don’t need this extra layer of simple value objects and I 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 stick 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 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, for one, am happy with the evolution of Doctrine, because I favor Data Mapper over Active Record.
Installing Doctrine 2 and creating the PHP project
First of all you need to get the Doctrine framework. There are four different ways (PEAR , Package Download, GitHub, or SVN). Depending on what method you use the configuration of Doctrine will differ a little bit. I used GitHub for my project, and I pulled out the code outside of Apache’s web root.
Speaking of the project, I used Eclipse PDT with Flash Builder 4 (I installed the Flash Builder plug-in on top of Eclipse PDT). The first step after getting the Doctrine framework was creating a PHP project named students-doctrine2. Next I added Flex nature to the project by right-clicking on the project name and choosing Add/Change Project Type… > Add Flex project type from the contextual menu. Make sure you select PHP for the Application Server type and you fill in the path and URL for your web root.
The next step was to create a folder inside the web root where I’ll put the PHP services, entities, and Doctrine’s configuration files: doctrine2_students. Inside of it, I created three folders named entities, proxies, and services. And finally, I created a linked resource between doctrine2_students folder and my Eclipse project (right-click on the project, choose New > Folder and then click on Advance button and navigate to the folder location). With that I was ready to write PHP and Flex code.
The next step is to create a bootstrap file (I call it bootstrap.php and place it inside of the doctrine2_students folder) that configures Doctrine 2 to be usable with my project. This means to load the framework classes, set up database access information, specify entities location and annotation method, and configure the different caches that will be used by the app. In the same bootstrap file I created an instance of the EntityManager class. This is the entry point to the Doctrine 2. If you download the project source code (see the links from the second to last section) you’ll find the bootstrap.php file inside of the doctrine2_students folder. The file looks like this:
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 methods: annotations, YAML, XML, and plain PHP. I initially favored the first one 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 I have four tables in my database and I need four entities for my application):
Using annotations you 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 property country_id of type int. Instead, I added a property called country that is of Country type. Similarly, I created a property called marks that holds an array of Mark entities – if a student attends three courses, then its marks property will hold an array of Mark objects with three instances.
Here are some notes on creating entities with Doctrine 2:
- With Doctrine 1 I used the built-in tools to create the domain model from the database structure
- Doctrine 2 has support to create the YAML out of the database schema, and then generate the entities; however I’m not sure 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
- You have to remember to set the properties as private/protected and not public, and add getters/setters. If you fail to do this, you might get nasty bugs (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. These will be the services used by the Flex client to get and persist data. Basically, using the Zend Framework I’ll be able to invoke remote procedure calls on these objects from the Flex side.
Inside the doctrine2_students/services/ folder I created four PHP files: CountriesService.php, CoursesService.php, MarksService.php, and StudentsService.php. I edited the bootstrap.php file to load the services folder along with the rest of the files. Here is the listing for the CountriesService class:
As you might expect, the complex code is inside of StudentsService class; here’s the public API:
As I said before, the entry point to Doctrine 2 is its EntityManager class. You can use this class to query for persistent objects using 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:
If you want to change the name for this country, you’d write this code:
If you want to create a new country, you’d write this code:
Whith 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:
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. And if you print_r() the result you’ll see a structure along these lines:
In addition, when you create these kinds of queries with fetch joins and you use getArrayResult() method on the query object instead of getResult(), you get a nice array or associative array of other arrays. These data are ready to be sent to the Flex client without any transformation.
The only part I haven’t discussed is how I handle the changes for a Student. I will talk about this as part of the Flex client.
For the communication between the Flex and PHP side I’ll use remoting. On the PHP side I need Zend AMF in order to enable remoting. But for the purposes of this article I will let Flash Builder 4 handle this (installing Zend Framework and creating the gateway for exposing the four PHP services I created).
Creating the Flex client
With the server code in place, it is time to add the Flex code. In the previous article I wrote all client code manually. This time I’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 getStuff() operations. Follow these steps:
- From the Data/Services view click the Connect to Data/Service link for the first service or on the third icon (the one with a + sign) for the other three services
- When the wizard opens, select PHP, and click Next. If it is your first time using the wizard for PHP, then you’ll be presented with a dialogue to install the Zend Framework
- Click Browse and select the first PHP service
- Repeat these three steps until you have defined all the four services. Please note that you can change the packages where the services and the value objects will be created
Now, it is time to define the value objects I’ll use on the Flex side. Again I’ll 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 the return types first with CountriesService and CoursesService, then for MarksService, and finally for StudentsService
- To define the return type for an operation on as service, expand the tree for the service, and right-click the operation (for example, select getCountries() from CountriesService) and choose Configure Return Type from the context menu. When the wizard opens, make sure the first option is selected on the first screen (Auto-detect the return type from the sample data) and click Next
- On the second page you can enter a name for the value object class (for example Country)
- 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 for the type column, the types you defined earlier (Course, Country, or Mark)
Here is how the Student value object is presented in the Data/Services view after finishing the above steps:
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. For this task I reuse most of the code I wrote in my previous application. I had to tweak some of the methods a little (for example, saveStudent() and onStudentSelection()).
When the application starts, the first thing I want it to do is load the courses, countries, and students. To do so, I created an init() function and registered it on the creationComplete event of the application. Then, I selected the getStudents() method from the Data/Services view, right-clicked it, and chose Generate Service Call. This command adds to the code an instance of StudentsService, an instance of CallResponder (you use this object to retrieve the result using the lastResult property or to register a result/fault listener for that operation), and 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:
Another change from the first article is the use of bidirectional binding for the firstName, lastName, and registration fields of the form.
Here is the complete code of the Main.mxml where the magic happens:
Now, when I call the saveStudent() method I make a call to the remote operation (saveStudent() from the StudentsService.php) and pass along an instance of the Student ActionScript class. In the PHP method (StudentsService->saveStudent()) I get an anonymous Object, so I had 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:
If you think it is way to much code for this “simple” operation, then I think you are partially right. In the Conclusions section I touch on this. However you have to remember that this code handles many things: creating a new student, and inserting its marks in the Marks2 table or updating a student and its marks from the Marks2 table if they were changed (new courses added, grade changes and so on).
Instead the delete method is quite clean (remember that behind the scene removes all the related records from the marks many-to-many table):
Getting the source code
If you’re curious and you want to explore the code, you can download the project from here. It contains the PHP doctrine2_students folder, the database SQL dump for creating the tables, and the Flex code.
The easiest way to import this project is just to extract the folders (you import the project in Flash Builder). Then place the PHP code (doctrine2_students folder) inside of the 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 the database credential. And finally, create a new Flex/PHP project, and repeat the first part of the article (creating the Flex wrapper services and value objects).
Overall, I think Doctrine 2 makes it easier to work on PHP and Flex projects. I especially love the new Data Mapper approach and its flexibility/power. The entities are very light and you can easily use DQL in conjunction with getArrayResult() to build a data structure ready for sending to Flex. Also there is no need for all the plumbing work I did for Doctrine 1 in order to send the objects on the PHP side. I encourage you to read the excellent documentation you’ll find on Doctrine site to better understand the inner workings, the different types of associations, and in general the features it offers.
With Doctrine 2 you get a big 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 the most the effort goes in. So it is a good thing to have a framework that standardize the PHP code and helps you big time when retrieving and persisting data. However, you can tell that it is not a framework architected with rich clients in mind (and there is nothing bad in this, I mean it’d be the same with the most of the frameworks out there). As easy it is retrieving data from the underlying persistence layer and sending them to a rich client, as hard persisting the client changes is. You have to write custom code to create the PHP entities out of the data received from the client before being able to 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 example 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 send associative arrays to Flex (which are deserialized into Objects in Flex).
A feature I stayed away instinctively (both with Dotrine 1 and 2) was the ability to generate the database schema using the entities and the mapping between them. In other words, you could start your project first writing the PHP data model, and then use Doctrine to generate the database for you. I’m old school and all my experience taught me that relational databases treat you well if you treat them well. Thus, I much prefer to create the database with my “own” hands and make sure I set all the indexes/constraints I need. Having said this, I’m not saying that the Doctrine feature for generating database schema is buggy or worthless. I just wanted to explain why I haven’t tried. Your mileage could vary on this one.
The data-centric development features of Flash Builder simplified the creation of the data model a lot. I’m not sure if they work for any kind of project, but in this case I didn’t see any drawbacks.
You have very little work to do when handling the Delete and Read part of the CRUD operations on the server side. However, with Create/Update things change, especially when the object has associations (many-to-many, one-to-many, many-to-one with other entities). I thought it would be enough to retrieve the existing Student data from 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:
Doctrine 2 offers a Cascade feature for persist/remove. For example, here I defined cascade delete/update for the Marks entities on the Student object:
However, I found that they actually work perfectly only on delete (see the previous paragraph for why I don’t believe the persist is fully functional). It is possible that I didn’t understand the usage, and I was expecting more than intended or I just messed up 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. So I had to alter the table and add 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, on the PHP side you get a Zend_Date object (when using the Zend Framework). And because Doctrine 2 knows how to handle only the PHP DateTime object, you have to manually handle the transformation. It would be cool to either configure in some way Zend Framework to use PHP DateTime instead of Zend_Date or to have Doctrine 2 handle this for you :D
I had tons of fun while writing this article and playing with this new technology. I can say that Doctrine 2 rocks! If you give Doctrine 2 a try with Flex, please drop a comment with your impressions.