Zend Framework Model and ORM

Using Zend Framework 1.10 and modular structure.

EDIT (January 2012): as of now Doctrine 2 has been out for a while and brings much better ORM solution than before – complete rewrite. I suggest to check it out instead, as the bellow info is at the moment little bit outdated.

With Zend Framework becoming ever so popular and improving, it is important to create your code using most recent and efficient techniques.

I have been looking for answers how to correctly implement Model and Object Relational Mapping for months but have found only incomplete or dated information. Many of development approaches and components are cover on numerous blogs or on Zend Framework QuickStart but some such as Model design or Object Relational Mapping (ORM) are still very unclear, so I have decided to write this article and share my implementation of this subject.

There is not only one way to do this, but I hope that approach I have taken is in right direction, and will help other people seeking answers.
You can also use other approach such as extra Service class or use of external “Doctrine” library as ORM but I believe that Zend framework on it’s own can do this job as good (or better) as any other.

First a bit of general view.

What is ORM

The official definition of ORM is:
Object-relational mapping (ORM) in computer software is a programming technique for converting data between incompatible type systems in object-oriented programming languages. This creates, in effect, a “virtual object database” that can be used from within the programming language.
The way I understand it, ORM is a way to map (assign) data from source (database, service or other resources) to object which then is used and manipulated with on it’s own.
This is done with mapper in middle so that there is clear separation of each other, meaning object has no knowledge of where the data are coming from, be it database, service, text file,…
ORM vizualisation

Are you asking why bother?
Well, this approach makes it very scalable and allows us to easily change data source with minimum effort. It saves time and money.

Model object is a container for data accessible in OOP manner and object class also contains methods for manipulation with that data.
Bellow are some example uses to better understand:

Model autoloading

To be able to work seamlessly we need to have our model classes available on the go.
We can take advantage of (module) resource auto loading in zend framework.
By looking at code of Zend_Application_Module_Autoloader we can see that application automaticly loads classes of models, DbTables and mappers whenever we need them:
Part of Zend_Application_Module_Autoloader:

So if your module name is “My” and you call My_Model_Customer it will look for file modules/My/models/Customer.php .
Same for mappers and DbTables:
My_Model_Mapper_Customer will look for file modules/My/models/mappers/Customer.php .
My_Model_DbTable_Customer will look for file modules/My/models/DbTable/Customer.php .

All we need to do to allow module autoloading is to place module bootstrap file into our module directory.
application/modules/My/Bootstrap.php:

Object (Model)

Now that we have the general overview we can dive straight into actual coding.

As we discussed, model object is a container for data accessible in OOP manner and object class also contains methods for manipulation with data.
Example uses:

First we create file in our model directory My/models/Customer.php

Our class will have property for holding data and several methods.
(note there can be any number of custom methods helping to get only specific data or records).

When creating or extending from model class we should fill up needed properties we expect to have:

Now that we have model class to hold our data let’s continue and make them to be accessible in OOP manner.
First we start with __get method. This method is called automatically when user requests property of object, for example: echo $object->property;

As you can see we first check if getter method exists in our class and return it, or
if not we check for key in our $_properties array and return value.
Getter method is method that starts with “get” and follows with property name (camelCased), for example: getUser or getUserName. It can contain extra functionality to manipulate with data before returning it. It is not necessary to use getter methods, but it is highly encouraged.
Now we can access our data as $object->property

Second we take __set method.
This method is called automatically when user wants to set property of object, for example:
$object->property = “value”;

Here we first check if setter method exists in our class and return it, or if not we check for key in our $_properties array assign value and return itself.
Setter method that starts with “set” and follows with property name (camelCased), for example: setUser or setUserName. Just as getter methods it can contain extra functionality to manipulate with data before assigning it. Again it is not necessary to use setter methods, but it is highly encouraged.
Now we can set our data as $object->property = “value”;

We now create method to add new properties to the object.
You could just skip this by not checking that the property exists in __set method
and when assigning value to non-existent property new one would be created,
but I like to have small security check against myself.
What if you misspell a property when coding and then further using object, you could end up with unpredicted behaviour and spend hours tracking down the mistake.
Here is the method:

Next we should include methods __isset and __unset .
These methods are called when we want to find out if particular property is set ( if(isset($object->property)) ), or to unset the property ( unset($object->property) ).

Now that we have OOP accessible container to hold data from resource (database) we need to get them.
To do any kind of retrieving, saving or deleting of data on resource (database) we use mapper class.
First we need to add additional variable $_mapperClassName on top of class and one method to retrieve mapper.

And now to the next methods interacting with data.

find() method gets record with specified $id. It connects to mapper and calls it’s find method.
fetchAll() method gets all records.

In fetchAll() method we retrieved all records, but what if we want to retrieve only records that match some value in certain property? We could create method similar to fetchAll() and add WHERE clause, but that would not make it true separation of elements in ORM style.

We could also create method for each of the scenario (fetchAllBy..,..), but number of combinations and therefore methods would be enormous and in we would be repeating ourselfs over and over.
We rather create functionality called “Magic methods”. It is one method that handles all.

If a class implements __call(), and if an object of that class is called with a method that doesn’t exist method __call() is called instead.
That is where we start.

Let’s prepare our __call method for handling findAllBy’Property’ calls where ‘Property’ can be any property in object (camelCased), eg.: findAllByUsername or findAllByStatus,..
Note, you can continue building on same principle and forward calls to more complex functions such as findAllByFieldLimitTo(), findAllWhere(), findAllByFieldSortAsc(), etc.
We may talk about these extensions in part 2.

Next is delete method.
This method deletes record matching our object.

And lastly the save method.
Saving method is a bit different.

First we check for ID, then depending on that we call pre functions (I like to include updated or created property together with rest of data),
then we call mapper’s save method and return true or false depending if saving was successful.

We can also include few utility methods.
There can be number of usefull methods but for now let’s stick with these 2:

And that’s it for Object.

Resource (DbTable)

Resource can be any database, data service, or even simple data from text file.
In our case let’s use Zend_Db_Table.
Implementation is very simple and basic:

We extend our class from Zend_Db_Table and add protected property $_name to let the base class know name of the table to use.
I like my database table fields to have a prefix starting with singularised name of table except for id. For example in “customers” table we have field “customer_name”.
I also like to have any multi-word separated by underscore. For example “customer_last_login”.
Mapper class will include code to reflect these choices.

Here is sql to create table.

Mapper

Next step is to create mapper class.
Mapper is a glue piece that connects(maps) resource data to object.
Mapper can also be used by many objects or be extended on.

This is how our basic mapper will look like.

First we need to let the mapper class know what resource it should use.

We create protected $_resource and $_resourceClassName variable.
First will hold actual resource object and second name of the class to use.
Next we need to create getResource method to get resource object.

As discussed earlier object calls mapper method and mapper translates and forwards request to resource or gets response from resource, translates it and send data to object.
So now we need to create every method object will call.

First on the menu is find() method.
We need to get data from resource that has id equal to our method argument.
We first check that argument is in required format (integer), create query and retrieve record from resource (in our case dbTable).
We then strip table prefix from field names and assign all values to object.

FetchAll returns all records from resource and creates new object from them. This method has argument $objectClassName which is used to make new objects.

In fetchAll() method we have retrieved all records from resource. Now if we want to retrieve only records that match some value in certain property we could as mentioned earlier create method similar to fetchAll() and add WHERE clause, but that would not make it true separation of elements in ORM style.

We could also create method for each of the scenario (fetchAllBy..,..), but number of combinations and therefore methods would be enormous and in our case where resource is DbTable we would be repeating ourselfs over and over.
We rather create same functionality as in object class called “Magic methods”.

As we said earlier, if a class implements __call(), and if an object of that class is called with a method that doesn’t exist method __call() is called instead.

Let’s prepare our __call method for handling findBy’Property’ calls where ‘Property’ can be any property in object (camelCased), eg.: findAllByUsername or findAllByStatus,..
Note, you can continue building on same principle and forward calls to more complex functions as findAllByFieldLimitTo(), findAllWhere(), findAllByFieldSortAsc(), etc.
We may talk about these extensions in part 2.

And now the protected method that converts our request and gets results from database/service.
It is basically modified fetchAll method.

Save method takes object, converts it to right format (array in this case) and if object has id it finds record in database and updates it or creates new record.

Delete method deletes record from resource (database).

footer

In this article we went through creating fully functional basic implementation of ORM the zend framework way. Feel free to use ideas and code in this article, just please retain the copyright notice.

Here is the code used in the article.

In next article we take a look at some of possible enhancements of our model.
Such as:
Data relationships,
Separation of content properties from main properties,
Lazy loading of content data,
Database design,
Extending our model class from countable or iterable,
or anything else that comes on my mind.

It should be available within few weeks/months (depending on my busy life).

Check out 2 project management software I use.
collabtive

TeamworkPM

12 thoughts on “Zend Framework Model and ORM”

  1. Pingback: Zend and ORM
  2. Beneficial info and excellent design you got here! I want to thank you for sharing your ideas and putting the time into the stuff you publish! Great work!

    1. Hello Brian, Thank you.
      I don’t like doctrine much either so hoping to help in presenting that many of functionality can be done with Zend Framework itself.
      I will do my best to publish second part soon. Hopefully before Christmas.

  3. Sorry for double reply, but if possible can you cover model/object caching and models consists of multiple tables. e.g. we are allowing customers’ to save multiple email addresses and we save those email addresses in a 2nd table so the database can handle unlimited emails for a single customer. In this case the customer model will have a array/collection of email object so we can perform any email related functions. Another Important feature that you can cover is pagination support.

    1. Hello Bryan.
      Yes the second request will be covered in part 2 plus extra features of it such as lazy loading (loading from second table only when needed).
      Caching and pagination may have to wait for part 3 ;-). I appologize, my days are very hectic but hoping to write it before Christmas.
      David.

  4. Hey there, this is a fantastic post. I’m going to e-mail this to my pals. I came on this while exploring net I’ll be sure to come back. thanks for sharing.

  5. Hi David,
    I was quite happy to see this tutorial because model implementation in Zend Framework is one concept I have difficulty grasping especially since the ZF documentation does not really touch on models except showing you how to use Zend_Db.

    I look forward to more of your articles.

    Thank you

  6. Very nice article, learned a lot of it! It’s a pitty Zend doens’t provide this kind of tutorials as ‘good practice of Zend_Db’. A model is related to the specific application, but it’s very hard to get started with Zend models.

  7. Great article, but Zend_Filter::filterStatic will slow mapping down. In case of over 100 entries and over 30 db table columns, the process will go over 10 sec.

  8. Hello, David.
    Nice article, I was very annoyed when get known that there is no native support for ORM. Before I was using Symfony framework, but the version 2.0 now has Smarty integrated in it as well as some components of Zend. So I decided to switch to Zend. By the way how long do you know Zend framework? I’d lovely watch different patterns from you :)

Leave a Reply