Autoloading Doctrine and Doctrine entities from Zend Framework
A number of people on the mailing list and twitter recently have asked how to autoload Doctrine using Zend Framework's autoloader, as well as how to autoload Doctrine models you've created. Having done a few projects using Doctrine recently, I can actually give an answer.
The short answer: just attach it to Zend_Loader_Autoloader
.
Now for the details.
First, make sure the path to the Doctrine.php
file is on your include_path
.
Next, Zend_Loader_Autoloader
allows you to specify "namespaces" (not actual
PHP namespaces, more like class prefixes) it can autoload, both for classes it
will autoload, as well as for autoload callbacks you attach to it. Typically,
you include the trailing underscore when doing so:
$autoloader = Zend_Loader_Autoloader::getInstance();
$autoloader->registerNamespace('Foo_');
$autoloader->pushAutoloader($callback, 'Bar_');
However, because Doctrine has a master class for handling common operations,
"Doctrine", we have to omit the trailing underscore so that the Doctrine
class
itself may be autoloaded. We need to do two different operations: first, add a
namespace to Zend_Loader_Autoloader
for Doctrine (which will allow us to
autoload the Doctrine class itself, as well as the various doctrine subcomponent
classes), and then register the Doctrine autoloader (which will be used by
Doctrine to load items such as table classes, listeners, etc.):
$autoloader->registerNamespace('Doctrine')
->pushAutoloader(array('Doctrine', 'autoload'), 'Doctrine');
This takes care of the Doctrine autoloader; now, let's turn to Doctrine models.
First, tell Doctrine that you want to autoload. You do this by telling it to use "conservative" model loading (shorthand for lazyloading or autoloading), and to autoload table classes:
$manager = Doctrine_Manager::getInstance();
$manager->setAttribute(
Doctrine::ATTR_MODEL_LOADING,
Doctrine::MODEL_LOADING_CONSERVATIVE
);
$manager->setAttribute(
Doctrine::ATTR_AUTOLOAD_TABLE_CLASSES,
true
);
From here, you need to ensure you actually can autoload the models. Normally, you tell Doctrine where to find models, but we're in a Zend Framework application, so let's leverage ZF conventions.
I typically put my model code with my application code:
application
|-- Bootstrap.php
|-- configs
|-- controllers
|-- models <- HERE
|-- modules
| `-- blog
| |-- Bootstrap.php
| |-- controllers
| |-- forms
| |-- models <- HERE
| |-- services
| `-- views
`-- views
Zend Framework already provides mechanisms for autoloading application resources
via Zend_Loader_Autoloader_Resource
and Zend_Application_Module_Autoloader
.
Assuming you've extended Zend_Application_Module_Bootstrap
in your module
bootstraps, you're basically already set. The trick has to do with your table
classes; your table classes must be placed in the same directory as your
models, and they must be named exactly the same as your models, with the
suffix "Table".
For example, if you had the class Blog_Model_Entry
extending Doctrine_Record
in the file application/modules/blog/models/Entry.php
, the related table class
would be Blog_Model_EntryTable
in the file
application/modules/blog/models/EntryTable.php
.
I automate most of this setup via my Bootstrap
class, which typically looks as
follows:
class Bootstrap extends Zend_Application_Bootstrap_Bootstrap
{
protected function _initAppAutoload()
{
$autoloader = new Zend_Application_Module_Autoloader(array(
'namespace' => 'App',
'basePath' => dirname(__FILE__),
));
return $autoloader;
}
protected function _initDoctrine()
{
$this->getApplication()->getAutoloader()
->pushAutoloader(array('Doctrine', 'autoload'));
$manager = Doctrine_Manager::getInstance();
$manager->setAttribute(Doctrine::ATTR_AUTO_ACCESSOR_OVERRIDE, true);
$manager->setAttribute(
Doctrine::ATTR_MODEL_LOADING,
Doctrine::MODEL_LOADING_CONSERVATIVE
);
$manager->setAttribute(Doctrine::ATTR_AUTOLOAD_TABLE_CLASSES, true);
$dsn = $this->getOption('dsn');
$conn = Doctrine_Manager::connection($dsn, 'doctrine');
$conn->setAttribute(Doctrine::ATTR_USE_NATIVE_ENUM, true);
return $conn;
}
}
Within your configuration, you need to add two keys: one for registering the Doctrine namespace with the default autoloader, and another for the dsn:
autoloaderNamespaces[] = "Doctrine"
dsn = "DSN to use with Doctrine goes here"
I also have a script that I use to load all model classes at once in order to do things like generate the schema or test interactions. I'll blog about those at a later date. Hopefully the above information will help one or two of you out there trying to integrate these two codebases!
Updates
- 2009-08-21: added information about registering Doctrine namespace with default autoloader