Medryx Observations

October 4, 2008

CodeIgniter and Doctrine

Filed under: PHP — Tags: , , , — maulin @ 9:47 am

The most widely sited source for how to bootstrap Doctrine with CodeIgniter is available on the CI Wiki. While this approach is effective, I found it a bit intrusive into the CI code. I didn’t like the idea of having to edit index.php, and I didn’t like that the database.php, which should really only contain configuration code, was hijacked to actually do the bootstrapping. So I looked into it a bit, and I think there is a cleaner (if not easier) way.

The goal when bootstrapping Doctrine is to load up your database connection and point to your models folder. Thats pretty much it. Once you do that, all your models autoload, so you don’t need to do things like $this->load->mode('User'). Instead you can simply use Doctrine’s tools such as new User() or Doctrine::getTable('User').

So here is the process:

1. Install a “hook” that will bootstrap Doctrine. Edit application/config/hooks.php and add the following:

$hook['pre_controller'][] = array(
	'function' => 'bootstrap_doctrine',
	'filename' => 'doctrine.php',
	'filepath' => 'hooks'
);

2. Copy the following to application/hooks/doctrine.php

// YOU MUST EDIT THIS TO BE THE PATH TO YOUR DOCTRINE LIBRARY
// I put mine in a 'libs' folder at the same level as the 
// CI application folder, but it can be anywhere.
require_once APPPATH   	   . DIRECTORY_SEPARATOR . 
				'..' 	   . DIRECTORY_SEPARATOR . 
				'libs'     . DIRECTORY_SEPARATOR . 
				'Doctrine' . DIRECTORY_SEPARATOR . 
				'Doctrine.php';
 
function bootstrap_doctrine() {
 
	@include APPPATH . DIRECTORY_SEPARATOR . 'config' . DIRECTORY_SEPARATOR . 'database' . EXT;
 
	// Set the autoloader
	spl_autoload_register(array('Doctrine', 'autoload'));
 
	//optional, you can set this to whatever you want, or not set it at all
	Doctrine_Manager::getInstance()->setAttribute('model_loading', 'aggressive');
 
	// Load the Doctrine connection
	// (Notice the use of $active_group here, to make it easy to swap out
	//  you connection based on you database.php configs)
 
	if (!isset($db[$active_group]['dsn'])) {
		//try to create the dsn, if it has not been manually set
		//in your config. I personally would opt to set my
		//dsn manually, but it works either way
		$db[$active_group]['dsn'] = $db[$active_group]['dbdriver'] . 
                        '://' . $db[$active_group]['username'] . 
                        ':' . $db[$active_group]['password']. 
                        '@' . $db[$active_group]['hostname'] . 
                        '/' . $db[$active_group]['database'];
	}
 
	Doctrine_Manager::connection($db[$active_group]['dsn']);
 
	// Load the models for the autoloader
	// This assumes all of your models will exist in you
	// application/models folder
	Doctrine::loadModels(APPPATH . DIRECTORY_SEPARATOR . 'models');
}

3. Done. Doctrine and its models are now available in the usual Doctrine way in all of your controllers.

Let me know if you have questions!

15 Comments »

  1. Hello!

    Great article!

    I made exactly that, but I get the message:
    Fatal error: Class ‘Model’ not found in /var/www/project2/system/application/models/my_model.php on line 2

    do you know how can I resolve?

    thanks

    Comment by Ricardo — October 9, 2008 @ 1:19 pm

  2. So, you have a my_model.php file that extends Model? That does not appear to be a typical Doctrine model class. First, you do not need the MY_ prefix for classes in your models folder. Secondly, if you are making a Doctrine model, then you need to extend Doctrine_Record in your class. So if you did want a class called my_model, you could do:

    class MyModel extends Doctrine_Rectord {
    function setTableDefinition() {
    //set up your model fields
    }
    }

    Then, assuming you inserted the bootstrap technique above, you could access your MyModel class in your controller using:

    $model = new MyModel();

    or

    Doctrine::getTable(’MyModel)->findAll()

    You get the idea…

    Comment by maulin — October 10, 2008 @ 12:48 am

  3. Hey,

    I’m trying to integrate Doctrine into a new CI app I’m working on. I’m getting the error…

    Uncaught exception ‘Doctrine_Table_Exception’ with message ‘Class “CI_Base” must be a child class of Doctrine_Record’ in C:\Users\Tom\Desktop\xampplite\htdocs\gkci\lib\doctrine\Doctrine\Table.php:288

    Any idea on how to fix this?

    Thanks for the help!

    Comment by Tom — October 20, 2008 @ 5:23 pm

  4. Hey, I solved my problem.

    Some of my doctrine models were named the same as my CI controllers.

    I named my Code Ignitor controller Account, which there was also a doctrine model named Account, so these two class names were conflicting

    Solved this issue by simply changing the CI controller to Account(s) with an s at the end.

    Hope this helps anyone that got stuck on the same issue.

    Tom

    Comment by Tom — October 20, 2008 @ 7:28 pm

  5. I think this is a great improvement from the Wiki method as I also hate to hack the core

    I was thinking nonetheless, by applying this method you’re always Autoloading Doctrine, which might not be what desired in some cases.
    Wouldn’t i make sense to use a plugin instead of a hook ?

    In that way you can call
    [code]
    $this->load->plugin(”doctrine”);
    [/code]

    whenever you really need it

    Comment by barbazul — November 8, 2008 @ 11:48 pm

  6. I’m glad I have moved on from CodeIgniter and Doctrine to Kohana, which is like CI in a lot of ways but is PHP5 and comes with great ORM…

    Check it:
    http://docs.kohanaphp.com/libraries/orm

    Comment by john — November 20, 2008 @ 2:38 am

  7. Hello and thanks for an informative post.
    How would you setup the cl interface, based on that method?

    Comment by dxrsm — December 5, 2008 @ 12:53 am

  8. [...] March 28 2009 Zaki saved Stop Caching Old Files - A PHP Function and CodeIgniter and Doctrine on Delicious. 02:00 am - No [...]

    Pingback by links for 2009-03-27 — Mior Muhammad Zaki: PHP & JavaScript Programmer — March 27, 2009 @ 7:26 pm

  9. hi there,

    Great article. Well I am trying to use Doctrine as you said. But am getting an errror:

    Fatal error: Class ‘abc’ not found in F:\wamp\www\system\application\controllers\lab.php on line 11

    i am clueless. I am using 1.7.2 version of ci. I have also populated the database field in config

    Comment by Codemaster Snake — October 7, 2009 @ 9:16 pm

  10. btw here is the code of my model abc.php under modles flder

    <?php

    class abc extends Doctrine_Record {
    function setTableDefinition() {
    //set up your model fields
    }
    }

    Comment by Codemaster Snake — October 7, 2009 @ 9:18 pm

  11. It works and much better than editing index.php and database.php file. Thanks for sharing..

    Comment by endless beginning — May 11, 2010 @ 10:54 am

  12. i wonder with this technique, how do i autogenerated class models using cli.
    this code is cleaner but the code in wiki have capability to autogenerated doctrine class model.

    Comment by netfusion — May 22, 2010 @ 2:59 pm

  13. Netfusion, I’m also struggling with this, but apparently you can still get the command line working with the method described at the CI-wiki. In system/application/doctrine.php simply replace:

    require_once(’config/database.php’); with:

    // define constants used in hooks/doctrine.php, for instance
    define ( APPPATH, dirname(__FILE__) );
    define ( EXT, ‘.php’);

    // include and bootstrap
    require_once(’hooks/doctrine.php’);
    $doctrine = bootstrap_doctrine();

    keep the rest as it is and the command line seems to work … ( I hope ;-) )

    Comment by chaosAgain — May 29, 2010 @ 7:38 pm

  14. Great post. It’s good to hook up the charset and collation values from CI’s config/database.php

    $conn = Doctrine_Manager::connection($db[$active_group]['dsn']);
    $conn->setCharset($db[$active_group]['char_set']);
    $conn->setCollate($db[$active_group]['dbcollat']);

    Also, it’s also nice to import all connection, not just the active one:

    foreach ($db as $connection_name => $db_values) {
    $dsn = $db_values['dbdriver'].’://’.$db_values['username'].’:’.$db_values['password'].’@’.
    $db_values['hostname'].’/’.$db_values['database'];
    $conn = Doctrine_Manager::connection($dsn, $connection_name);
    $conn->setCharset($db_values['char_set']);
    $conn->setCollate($db_values['dbcollat']);
    }

    Comment by Matt Alexander — September 15, 2010 @ 1:24 am

  15. Hi,

    I would add the following steps and modifications to the tutorial:

    Step 2.

    Make sure you have the following in system/applications/hooks/doctrine.php:

    spl_autoload_register(array(’Doctrine’, ‘autoload’));
    spl_autoload_register(array(’Doctrine’, ‘modelsAutoload’));

    The second is necessary to load the models, I have seen the two lines together in other related tutorials.

    Step 3.

    In system/application/config/config.php replace the FALSE by TRUE to have:

    $config['enable_hooks'] = TRUE;

    Step 4.

    In system/application/config/hooks.php, remove the following condition:

    if ( ! defined(’BASAEPATH’)) exit(’No direct script access allowed’);

    I know it’s not a good idea, but I was not able to find an alternative. Unless a guru can help, this the only way I found. There are two remedies to remedy the removal of this safety check:

    1. use a .htaccess to protect the system folder
    2. place the CI system folder outside the DOCUMENT_ROOT of web server.

    If there is a better way, please provide corrections.

    Cheers.

    Comment by ramin — October 30, 2010 @ 6:30 pm

RSS feed for comments on this post. TrackBack URL

Leave a comment

Powered by WordPress