LogoPhly, boy, phly
the weblog and site of Matthew Weier O'Phinney

Thursday, August 20. 2009

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
Posted by Matthew Weier O'Phinney in PHP at 15:17 | Comments (19) | Trackbacks (7)
Defined tags for this entry: doctrine, php, zend framework
Related entries by tags:
Module Bootstraps in Zend Framework: Do's and Don'ts
Responding to Different Content Types in RESTful ZF Apps
Symfony Live 2010
Creating Re-Usable Zend_Application Resource Plugins
Quick Start to Zend_Application_Bootstrap

Trackbacks
Trackback specific URI for this entry

Integrating Zend Framework with Doctrine ORM
Yet another how-to for Zend Framework. You can find a lot of integration how-tos around the web with googling and I maybe the just one of them. I?m trying to find the wheel again. This one is like my subversion how-tos, a self reminder first. So let?s cut
Weblog: Kartaca Personel's Blog
Tracked: Dec 04, 08:10
Integrating Zend Framework with Doctrine ORM
Yet another how-to for Zend Framework. You can find a lot of integration how-tos around the web with googling and I maybe the just one of them. I?m trying to find the wheel again. This one is like my subversion how-tos, a self reminder first. So let?s cut
Weblog: Kartaca Blog's
Tracked: Dec 05, 07:19
Dutch PHP Conference 2009 wrapup
A couple of days ago I came home from one of the best PHP conferences in the world, Dutch PHP Conference 2009 with again a very elite list of speakers.Day 0: Tutorial DayThursday, June 11 was tutorial day. A whole day dedicated at learning new things by t
Weblog: All news about PHP
Tracked: Dec 21, 08:39
Zend Framework Bughuntday review
This Saturday user groups of the Netherlands (phpGG) and Belgium (PHPBelgium) organized Bughuntday at Best Western hotel "De Goderie" in Roosendaal (the Netherlands). Bughuntday is a whole day developers can come together to start fixing bugs for open-sou
Weblog: All news about PHP
Tracked: Dec 21, 09:50
ZendCon 2009 Review
If you're involved with PHP and you haven't heard of ZendCon before, you have been around the wrong croud!The past week was reserved for the biggest PHP event of the year bringing you the best of the best the open-source and commercial world have to offer
Weblog: All news about PHP
Tracked: Dec 21, 20:10
A great 2009 !
It's that time of year again where one looks back to all the things that have happened in the past 12 months. 2009 has to be the most thrilling, fastest ride ever and I'm really surprised to see it's nearly the end of this year.PHP
Weblog: All news about PHP
Tracked: Dec 29, 06:57
Zend Framework and Doctrine Part 1
I’ve begun to investigate the Doctrine ORM library and how to integrate it into Zend Framework applications. I figure this post and any subsequent ones on the topic can grow into some sort of discovery series. So, my first post is going to be a s...
Weblog: More Cowbell
Tracked: Jan 12, 21:14

Comments
Display comments as (Linear | Threaded)

I was wondering, where would you keep all your schemas, sql and migrations? Also, how would you setup your cli script?

im doing something like,
$application = new Zend_Application('development', '../configs/application.ini');
$application->bootstrap('Doctrine');
$cli = new Doctrine_Cli(Zend_Registry::get('doctrine_config'));
$cli->run($_SERVER['argv']);

would there be a better way?
#1 christian (Link) on 2009-08-20 21:07 (Reply)
That'll have to be the subject for another blog post. :-)

I've been typically generating my schemas and creating my database via a script, and I keep that in the scripts/ subdirectory of my project. However, I don't use the Doctrine CLI; I simply call out to the Doctrine methods I need to use for a given task. I'll post on that process soon.
#1.1 Matthew Weier O'Phinney (Link) on 2009-08-21 07:20 (Reply)
For me pushingAutoloader didn't work (Warning: call_user_func() expects parameter 1 to be a valid callback, [...]\lib\zf\Zend\Loader\Autoloader.php on line 125

The solution was to put `require_once 'Doctrine.php';` at the beginning of _initDoctrine()
#2 e.s.t on 2009-08-21 10:00 (Reply)
read this:

"First, make sure the path to the Doctrine.php file is on your include_path. "
#2.1 vistahr on 2009-08-21 10:49 (Reply)
ok,your right ... my fault.

You can put the Doctrine.php file to the Doctrine folder.
Like:
+ library
+ Doctrine
+ Doctrine.php

Now, u can setup the Autoloadernamespace ŽDoctrineŽ.

With Zend Application :
autoloadernamespaces.Doctrine = "Doctrine"


That works fine ;-)
#2.2 vistahr on 2009-08-21 11:13 (Reply)
Ooops! My bad -- I knew I was forgetting something when I wrote this. I've updated the article now to add the bit about registering the Doctrine namespace with the default autoloader.
#2.2.1 Matthew Weier O'Phinney (Link) on 2009-08-21 11:20 (Reply)
and so my method looks like this:

protected function _initDoctrine() {
$this->getApplication()->getAutoloader()->registerNamespace('Doctrine')
->pushAutoloader(array('Doctrine', 'autoload'));
$options = $this->getOption('doctrine');
$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);
Doctrine::loadModels($options['models_path']);
$prefix = null;
if(isset($options['database_prefix'])) $prefix = $options['database_prefix'];
$dsn = $options['dns'] . $prefix . $options['database_main'];
$conn = Doctrine_Manager::connection($dsn);
$conn->setAttribute(Doctrine::ATTR_USE_NATIVE_ENUM, true);
return $conn;
}

this way I can also use doctrine CLI Script, exactly as described in http://www.danceric.net/2009/06/06/doctrine-orm-and-zend-framework/

and everything works. Ah.. life is good.. :-)
#3 e.s.t on 2009-08-21 12:10 (Reply)
The one problem with what you present is that it doesn't allow for separating your model classes by modules -- which is why I'm not using the loadModels() method.
#3.1 Matthew Weier O'Phinney (Link) on 2009-08-21 12:12 (Reply)
so instead of using loadModels() I should use Zend_Application_Module_Autoloader as showed in _initAppAutoload() ?
#3.1.1 e.s.t on 2009-08-21 12:18 (Reply)
Yes and no. Within your application, yes. When using the Doctrine CLI and/or doing Doctrine ops such as generating the schema from the models, no. This latter topic I'll blog on soon.
#3.1.1.1 Matthew Weier O'Phinney (Link) on 2009-08-21 12:41 (Reply)
Waiting impatiently :-)
#3.1.1.1.1 e.s.t on 2009-08-21 13:02 (Reply)
me too :-)
#3.1.1.1.2 dave on 2009-08-21 20:12 (Reply)
Me as well - I am curious how I can use Doctrine to its full potential...
#3.1.1.1.3 nickrouty (Link) on 2009-08-27 17:39 (Reply)
I really can't figure out how to do that. Can anyone give me a hint? Right now I'm using Doctrine::loadModels() to make everything work. Thanks.
#3.1.1.2 Franco on 2009-09-20 11:11 (Reply)
Hi,

in my _iniDoctrine, I don't return $conn.
I return my $doctrine_options.

so, in my cli script:

$bootstrap = $application->getBootstrap();
$bootstrap->bootstrap('doctrine');
$doctrine_opt = $bootstrap->getResource('doctrine');

$doctrine_cli = new Doctrine_Cli($doctrine_opt);
$doctrine_cli->run($_SERVER['argv']);
#4 Joff on 2009-08-25 17:07 (Reply)
Thanks. Nice one (as usual).
Just wanted to let know, we are waiting for the promised next part covering CLI configuration.

Any tips about PHPUnit testing?
#5 admirau on 2009-09-01 08:16 (Reply)
Does anyone have a good way of specifying a connection to a specific model? I have read up on component binding, but still am not sure of the best way to implement. I will include some code to let you know where I am coming from:

In my bootstrap:

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::ATTR_QUOTE_IDENTIFIER,
Doctrine::MODEL_LOADING_CONSERVATIVE
);
$manager->setAttribute(Doctrine::ATTR_AUTOLOAD_TABLE_CLASSES, true);

$connections = $this->getOption('doctrine');

foreach($connections as $name => $dsn) {
$conn = Doctrine_Manager::connection($dsn, $name);

$conn->setAttribute(Doctrine::ATTR_USE_NATIVE_ENUM, true);
$conn->setCharset('utf8');
}


return $conn;
}

Which iterates through each connection in my application.ini:

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Initialize Databases
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

doctrine.db1 = "mysql://user:pass@localhost/db1"
doctrine.db2 = "mysql://user:pass@localhost/db2"

Should I extend Doctrine_Table or Doctrine_Manager and add some class? What I want to be able to do is just set a property in my Base class that's like $_conn = 'db1', and have it be bound so whenever I call that class, boom! I get the connection and we are off!

Any insight would be awesome... Thanks
#6 nickrouty (Link) on 2009-09-01 19:16 (Reply)
Problem Solved:

http://blog.routydesign.com/?p=62
#6.1 nickrouty (Link) on 2009-09-04 16:48 (Reply)
This solution didn't work for me when using Doctrine::getTable('MYTABLE')

You also need to call add the model autoloader:

$autoloader->registerNamespace('Doctrine')->pushAutoloader(array('Doctrine', 'modelsAutoload'), '');
#7 Behzad on 2010-02-05 13:34 (Reply)

Add Comment

Standard emoticons like :-) and ;-) are converted to images.
E-Mail addresses will not be displayed and will only be used for E-Mail notifications

To prevent automated Bots from commentspamming, please enter the string you see in the image below in the appropriate input box. Your comment will only be submitted if the strings match. Please ensure that your browser supports and accepts cookies, or your comment cannot be verified correctly.
CAPTCHA

 
 
  • Home
  • Resume
  • Blog
  • Phly PEAR Channel
  • Contact Me
  • About this site

ZCE

Zend Education Advisory Board Member

Add to Technorati Favorites

Calendar

Back March '10 Forward
Mon Tue Wed Thu Fri Sat Sun
1 2 3 4 5 6 7
8 9 10 11 12 13 14
15 16 17 18 19 20 21
22 23 24 25 26 27 28
29 30 31        

Quicksearch

Links

  • PHLY - PHp LibrarY
  • Paul M. Jones
  • Mike Naberezny
  • Shahar Evron
  • Planet PHP
  • Zend Where I now work
  • Garden.org Where I once worked

Archives

March 2010
February 2010
January 2010
Recent...
Older...

Categories

XML Linux
XML Personal
XML Aikido
XML Family
XML Programming
XML Dojo
XML Perl
XML PHP

All categories

Syndicate This Blog

XML RSS 0.91 feed
XML RSS 1.0 feed
XML RSS 2.0 feed
ATOM/XML ATOM 0.3 feed
ATOM/XML ATOM 1.0 feed
XML RSS 2.0 Comments

Show tagged entries

xml best practices
xml books
xml conferences
xml cw09
xml decorators
xml dojo
xml dpc08
xml file_fortune
xml git
xml linux
xml mvc
xml oop
xml pear
xml perl
xml personal
xml php
xml phpworks08
xml programming
xml rest
xml ubuntu
xml vim
xml webinar
xml zendcon
xml zendcon08
xml zendcon09
xml zend framework
© 2004 - present, Matthew Weier O'Phinney
matthew-web <at> weierophinney.net