In yesterday’s article of our advent calendar, we explained the concepts underlying Doctrine 2. In today’s article, we want to use that knowledge and create a simple Zend Framework (ZF) application that uses Doctrine 2 to persist its business objects.
While explaining how to integrate Doctrine 2 into Zend Framework, we will create a generic sandbox that can be used for future projects building up on these technologies.
Setting up a ZF project with Doctrine 2
Since this article is not about how to setup a ZF project, the following steps will require you to have a clean but working project with
the usual folder structure.
To use Doctrine 2 you will have to get a copy of its source code first. You can get one either from the project’s website or by cloning the git repository. In my opinion, downloading the source package from the
website is a lot easier (you don’t have to take care of all the project’s git submodules etc.). So download and extract the archive into a
temporary directory. After that, move the folder
Doctrine from the archive’s root into the
library of your new
ZF application. Now, your project structure should look like this:
The first thing that will occur to you if you look at the Doctrine 2 source code is that 100% of the code is
namespaced (for a (German) introduction to namespaces have a look at the article „PHP 5.3 Features in Real Life“ from a colleague of mine). This is pretty nice for people who are not scared of new
language constructs but for many people that are using ZF it might look odd at first. I am sure that you will get familiar with the syntax
differences really fast, but ZF (1) does not – because it does support real namespacing yet (however this will change with version 2).
Therefore we have to do some additional work that enables us to autoload classes that reside in namespaces. Luckily, Doctrine 2 comes with
a neat set of helper classes bundled in the project Doctrine Common which
also includes a class loader that fulfills our needs. The class loader is called
Doctrine\Common\ClassLoader and is really easy to use:
This code snippet creates and registers a new class loader that looks for classes in the namespace Doctrine. These classes should
be placed in a folder called Doctrine which is part of the current include path. Every namespace separator will be transformed into a
directory separator when looking for the classes, so make sure to keep your class-, namespace-, file- and folder-names in sync!
I recommend putting the initialization of the class loader into your application’s bootstrap class because this way it can be used for cli
scripts, too. As you might have seen, there are several packages inside the Doctrine folder that we placed in your library.
The folders ORM, DBAL and Common contain the Doctrine 2 classes required by the framework. The fourth folder
Symfony contains some classes borrowed from the Symfony2 framework. To load all these classes,
we have to initialize multiple class loaders:
The initialization of the loaders responsible for Symfony2 and entity classes requires a second argument, that indicates the directory
in which the classes of the namespaces can be found. If you do not provide this argument, the class loader just looks in the directories of your
include path for folder names matching the given namespace.
Creating the Entity Manager
The next thing we will do is creating an entity manager. To do so, we have to initialize a configuration object (of the type
Doctrine\ORM\Configuration) and pass it to the entity manager. Since we want to create a project that can be configured easily,
we separate the configuration data from the initialization code. Let’s begin with the initialization code and add the required configuration
directives to our
application.ini file later.
Instead of describing each single option here, I add some detailed comments in the initialization code:
From now on, you can access the entity manager by requesting it from the
Zend_Registry. This should probably be done via a
dependency injection system, but this article will not cover how to implement dependency injection or even a dependency injection container.
To make this bootstrap code work, you will have to add the following directives to your
Basically, this is all you have to do to integrate Doctrine 2 into your ZF application. It is fully functional with this
configuration, so let us take a snapshot of the project now and declare it as our sandbox for future ZF-Doctrine 2 projects.
Next, I want to show you how to create entity classes within your project and how to initialize a database scheme from these entities.
Creating Entity Classes
If you have read my yesterday’s article, the following part will not
look very new to you. I will create two entity classes: User and Group which have a n:m relationship called membership.
To write create tables in the database for these entities, we will use Doctrine’s console tools which are really handy. Create a new
doctrine.php in your application’s
bin directory and copy the following code snippet into it.
This script sets up a
Zend_Application first, bootstraps it and creates a normal Doctrine 2 console. You might want to have a
look at the original console script which is included in the Doctrine 2 archive we downloaded before. I added the ZF initialization
to be able to use the application’s configuration here.
When you run the script now, the output should look like this:
All actions related to your database’s schema have the prefix
orm:schema-tool. Create your schema now by executing
$ php bin/doctrine.php orm:schema-tool:create
If you have no errors in your configuration, your database should have three new tables now:
Now you are ready to implement your application’s logic. You can write code that queries, modifies and creates user and group objects and have
all data persisted by Doctrine 2. To end this article, I just want to give you a feeling for how you might interact with
Doctrine 2 in your business logic.
I hope this article helps you setting up the infrastructure for your next project and covers everything you have to know to begin using
Doctrine 2. The sandbox we set up can be downloaded here. Make sure to include the current versions of ZF and
Doctrine 2 after downloading it.