BookDB – step 0.5: Modules AND Doctrine

Ok, so, after half a week-end spent integrating Zend Framework and Doctrine, I finally did it, combining two of the countless blog posts that offer the “ultimate” guide into integrating Doctrine into the ZF.

My main issue was that I also wanted modules with my fries. So whatever I wrote in Step 0 is either incomplete or wrong.

I got modules working first. That wasn’t very hard.

Go into your project folder and create a module, let’s say admin: zf create module admin and then zf create controller index index-action-included=1 admin. This won’t get you modules working, will just get you a folder and some files. You still need a bit of this new Application/Bootstrap and fancy autoloader magic.

Add this in your main Bootstrap:

<?php

class Bootstrap extends Zend_Application_Bootstrap_Bootstrap
{
     protected function _initAppAutoload()
     {
         $autoloader = new Zend_Application_Module_Autoloader(array(
             'namespace' => '',
             'basePath' => dirname(__FILE__),
         ));
         return $autoloader;
     }
}

This will provide a loader/name space for your main, default module.
You also need an empty Bootstrap class in a Bootstrap.php file in your module’s directory (application/modules/admin).

<?php
class Admin_Bootstrap extends Zend_Application_Module_Bootstrap
{
}
resources.frontController.moduleDirectory = APPLICATION_PATH "/modules"
resources.modules[] =

And add the above to the app ini file. That last line is not wrong, trust me. It loads ALL resources related to the modules.

Your models can be called like this (for mai module and the example admin one.)


$book = new Model_Book();

$adminBook = new Admin_Model_ABook();

Ok, you should now have a working modular application. Now, let’s try Doctrine, again, in a way that doesn’t break this setup.
The bootstrap part is pretty logical, resembles what I had before trying to get modules in.

protected function _initDoctrine(){

     $this->getApplication()->getAutoloader()
          ->pushAutoloader(array('Doctrine', 'autoload'));
     spl_autoload_register(array('Doctrine', 'modelsAutoload'));

     $doctrineConfig = $this->getOption('doctrine');
     $manager = Doctrine_Manager::getInstance();
     $manager->setAttribute(Doctrine::ATTR_AUTO_ACCESSOR_OVERRIDE, true);
     $manager->setAttribute(
         Doctrine::ATTR_MODEL_LOADING,
         $doctrineConfig['model_autoloading']
     );

     Doctrine_Core::loadModels($doctrineConfig['models_path']);

     $conn = Doctrine_Manager::connection($doctrineConfig['dsn'],'doctrine');
     $conn->setAttribute(Doctrine::ATTR_USE_NATIVE_ENUM, true);
     return $conn;
 }

Now, for the config part, add this to application.ini, after the lines from above

doctrine.dsn = "mysql://user:password@localhost/bookdb_php"
doctrine.data_fixtures_path = APPLICATION_PATH "/doctrine/data/fixtures"
doctrine.sql_path           = APPLICATION_PATH "/doctrine/data/sql"
doctrine.migrations_path    = APPLICATION_PATH "/doctrine/migrations"
doctrine.yaml_schema_path   = APPLICATION_PATH "/doctrine/schema/schema.yml"
doctrine.models_path        = APPLICATION_PATH "/models"

doctrine.generate_models_options.pearStyle = true
doctrine.generate_models_options.generateTableClasses = false
doctrine.generate_models_options.generateBaseClasses = true
doctrine.generate_models_options.baseClassPrefix = "Base_"
doctrine.generate_models_options.baseClassesDirectory =
doctrine.generate_models_options.classPrefixFiles = false
doctrine.generate_models_options.classPrefix = "Model_"

; Doctrine needs to use Aggesssive autoloading for the CLI to generate prefixed models appropriately
; MODEL AUTOLOADING: 1 = aggressive, 2 = conservative

doctrine.model_autoloading = 2

[doctrineCLI : production ]
doctrine.model_autoloading = 1

[staging : production]

[testing : production]
phpSettings.display_startup_errors = 1
phpSettings.display_errors = 1
doctrine.dsn = "mysql://user:password@localhost/bookdb_php"

[development : production]
phpSettings.display_startup_errors = 1
phpSettings.display_errors = 1

There are two important things here. First, Doctrine is instructed to create the Base models so that they comply with the Zend standards. This took the best part of a day (to figure out what was going on, not to find a solution, I did that downloading the application from here and looking into their code.)
This will get rid of the generated folder and instead create all base models in models/Base, define the main models as Model_Book and have them extend Model_Base_Book, and not as before, Book extends BaseBook.
The second important part is the bit that tells ZF to use lazy loading for the models BUT use “aggresive” loading when the doctrine-cli is used. That will tell Doctrine to load all .php files from the model folders and subfolders. Otherwise it won;t generate the sql files or the tables because it won’t have loaded the Base models at that moment.
So, all that is left to do is tell the doctrine-cli script to use this ini group, and not the production one.

// Define application environment
defined('APPLICATION_ENV')
 || define('APPLICATION_ENV', (getenv('APPLICATION_ENV') ? getenv('APPLICATION_ENV') : 'doctrineCLI'));

That should do it, finally. I should start writting some real models now. Let’s see how that goes.

Leave a Reply