Bootstrapping the Doctrine 2.0 Autoloader in Zend Framework
September 7th, 2009
Please Note: This post was based on the Alpha release of Doctrine2. They have since added Doctrine\Common\IsolatedClassLoader which (among other things) doesn’t automatically register itself so we no longer have to unregister it, making things much easier.
Doctrine 2.0 looks like it might finally be the ORM framework I have been seeking for PHP. While the older versions of Doctrine provided great functionality, they were too intrusive for my taste. I think an ORM should provide a true data mapper; in which the domain entities need know nothing about their persistence.
Matthew Weier O’Phinney has already posted about autoloading Doctrine in Zend Framework, but Doctrine2 presents some new challenges. Mainly that Doctrine2 is fully PHP5.3, including "real" namespaces, so its classes don’t follow the (current) Zend naming standard and the ZF autoloader won’t load them for us.
Good News, Doctrine provides its own autoloader that we can leverage to load its own classes.
Bad News, the Doctrine autoloader automatically registers itself with spl_autoload_register, causing the normal Zend loader to be forgotten (well, pushed down the stack, where it isn’t very useful).
Good News, it’s easy to remove the doctrine autoloader using spl_autoload_unregister, then push it onto the ZF autoloader stack, targeting the Doctrine namespace. Letting the ZF autoloader call it as necessary.
Enough jibber-jabber, how do we do all this? In the bootstrap! Adding this method to your Bootstrap.php will achieve what we want; adding the Doctrine autoloader to the Zend Framework autoloader queue for the "Doctrine\" namespace.
protected function _initDoctrine()
{
// Create the doctrine autoloader and remove it from the spl autoload stack (it adds itself)
require_once 'Doctrine/Common/ClassLoader.php';
$doctrineAutoloader = array(new \Doctrine\Common\ClassLoader(), 'loadClass');
spl_autoload_unregister($doctrineAutoloader);
// Fetch the global Zend Autoloader
$autoloader = Zend_Loader_Autoloader::getInstance();
// Push the doctrine autoloader to load for the Doctrine\ namespace
$autoloader->pushAutoloader($doctrineAutoloader, 'Doctrine\\');
}
We can use all of Doctrine’s classes anywhere else in our code.
use Doctrine\ORM;
$manager = EntityManager::create(array('driver' => 'pdo_sqlite', 'path' => ':memory:/'));
// ...
I still have a lot to learn and the documentation on 2.0 is a little sparse as yet, but this is a start.




Thanks for sharing. this is information a piece that I looking for.
Looking for your next tips on this integration, such as, how to integrating doctrine 2 cache and zend_cache
Comment by bandirsen — 2010-01-14 @ 19:29
Do you have to explicitly create the EntityManager throughout your code (ex. in your controllers)? That seems a little wrong (especially if you have to provide the configuration information each time). Maybe create the instance in the Bootstrap as well and store in Zend_Registry. Thoughts?
Also, I’m wondering what the “recommended” directory structure is going to be as well.
Comment by Jonathon — 2010-02-27 @ 08:00
@Jonathon No, I would definitely not be creating the EntityManager in the controllers. I wouldn’t even use it in the controller – it’s part of the model layer.
More likely, I would create it once and inject it into the service layer. How that could be done is an entire post on its own. The registry is one option, others could be a central service broker, or a full Dependecy Injection/Inversion of Control container.
Comment by Brenton Alker — 2010-04-28 @ 10:05
I have been creating the EntityManager in Bootstrap, and putting it in the Zend_Registry. This is a fatally flawed approach. As soon as you do some explicit transaction demarcation, you will discover that the entity manager is closed on the first PDOException. Therefore, all subsequent operations fail, until you create a new EntityManager. So bootstrap is wrong. A factory pattern is missing here. I am thinking about putting an EntityManager factory in the registry, from bootstrap, and then calling factory.create() from each controller. That at least allows the factory to encapsulate the configuration options and event manager, leaving the controllers to call a simple one-liner with no arguments.
Comment by Skip — 2010-08-05 @ 15:08