While playing a bit with Doctrine ORM (1.2.1) for PHP, I encountered problems with autoloading my model classes. The autoload mechanism is described here. But as far as I see they changed this behaviour in doctrine 1.2.x but they didn’t updated the documentation yet. So it took me a while to find a solution. Here are the steps to reproduce the problem and my solution for this. I have simplified the original steps from the doctrine documentation a bit.

First I created a `user` table in the database:

CREATE TABLE IF NOT EXISTS `user` (
    `id` BIGINT(20) NOT NULL AUTO_INCREMENT,
    `username` VARCHAR(255) COLLATE utf8_unicode_ci DEFAULT NULL,
    `password` VARCHAR(255) COLLATE utf8_unicode_ci DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci AUTO_INCREMENT=1 ;

In the next step I created the model classes using doctrine. First we’ll create a little bootstrap script that contains the database and doctrine configs and later on the model generator script itself.

<?php
    // bootstrap.php
 
    // make sure you have the doctrine package in your classpath (!)
    require_once 'Doctrine.php'
    spl_autoload_register(array('Doctrine', 'autoload'));
 
    $dsn = 'mysql:dbname=doctrine_test;host=127.0.0.1';
    $user = 'user';
    $password = 'secret';
 
    $dbh = new PDO($dsn, $user, $password);
    $conn = Doctrine_Manager::connection($dbh);

… and here comes the model genarator script:

<?php
    // generate_model.php
    require_once 'bootstrap.php';
    // this method does the dirty work
    Doctrine::generateModelsFromDb(
        'models', // the folder where the classes should be stored
         array('doctrine_test'), // array with database names
         array('generateTableClasses' => true) // array with options
    ); 
?>

Then I run the script via php-cgi and doctrine created the following model class files for me.

$ php generate_model.php
$ find models/ -type f | sort
models/generated/BaseUser.php
models/User.php
models/UserTable.php

All runs fine until here. Now I tried to write a bit of code to insert a new user into the database. As the doctrine documentation says we have to modify the bootstrap.php a bit to set the model loading option to ‚lazy-loading‘, and add the Doctrine::loadModels() call.

<?php
    require_once 'Doctrine.php';
 
    spl_autoload_register(array('Doctrine_Core', 'autoload'));
 
    $manager = Doctrine_Manager::getInstance();
 
    // set the model loading attribute to lazy loading
    // @see 
    $manager->setAttribute(
        Doctrine::ATTR_MODEL_LOADING, 
        Doctrine::MODEL_LOADING_CONSERVATIVE
    );  
 
    // load the model classes from the folders 'models'
    Doctrine::loadModels('models');
 
    $dsn = 'mysql:dbname=doctrine_test;host=127.0.0.1';
    $user = 'user';
    $password = 'secret';
 
    $dbh = new PDO($dsn, $user, $password);
    $conn = Doctrine_Manager::connection($dbh);

Now we write a simple save_user.php which uses the User model – class generated by doctrine and which should be ‚auto-included‘ by the Doctrine class. (Remember, we don’t need a require_once 'models/User.php')

<?php
// save_user.php
require_once 'bootstrap.php';
 
$user = new User();
$user->username = 'thorsten';
$user->password = 'secret';
 
$user->save();
 
printf("New user created. id: %d\n", $user->id);
?>

But when I run the save_user.php via cli I got the following error:

$ php save_user.php
Fatal error: Class 'User' not found in /var/www/doctrine/save_user.php on line 7

Boom!. The autoload mechanism seems not to work!

But thanks to the open source licence of doctrine I digged a bit in the code and found a change between the 1.1.x version of Doctrine::autoload and the version 1.2.x one. Remember, we have registered this method in boostrap.php as the autload method.

    // bootstrap.php
    // ...
    spl_autoload_register(array('Doctrine', 'autoload'));
    // ...

In prior 1.2 version the method was responsible for (auto)loading of Doctrine framework classes such as Doctrine_Manager and for autoloading the model classes. In the new version the method is only responsible for framework classes. But digging a bit more I found a solution :)

If you want to autoload your model classes via doctrine you have to register Doctrine::modelsAutoload() too. This method is responsible for model classes. Modify your boostrap.php..

    // bootstrap.php
    // ...
    spl_autoload_register(array('Doctrine', 'autoload'));
    spl_autoload_register(array('Doctrine', 'modelsAutoload'));
    // ...

And when you now run save_user.php…

$ php save_user.php 
New user created. id:1

.. the User class was auto imported by the doctrine framework.. :) .

Happy coding!

Leave a Reply