7

Integrating Doctrine Into Kohana 3 [Updated 21 Feb 2011]

Posted June 21st, 2010 (Updated 7 May 2011) in Database, PHP

Update Feb 21 2011: New module at a new home! See here for details

Upon the release of Kohana 3, one of the first things I wanted to do was install Doctrine. The following is a short tutorial on how to do. Keeping with Kohanas modular style, it will be placed in its own reusable module. Time to begin.

Create the following files/folders:
/modules/doctrine
/modules/doctrine/init.php
/modules/doctrine/classes
/modules/doctrine/classes/doctrine

Inside /modules/doctrine/classes/doctrine drop the official latest build (see here) of Doctrine such that Doctrines CHANGELOG, COPYRIGHT etc files etc are inside.

Enter the following into init.php. Note – this may not be the optimal bootstrap file – feel free to tweak to your hearts content.

<?php
	/* Doctrine integration */
	require Kohana::find_file('classes', 'doctrine/Doctrine');
	spl_autoload_register(array('Doctrine', 'autoload'));
	// Getting kohana configurations for doctrine
	$db = Kohana::config('database');
	// initializing manager
	$manager = Doctrine_Manager::getInstance();
 
	// we load our database connections into Doctrine_Manager
	// this loop allows us to use multiple connections later on
	foreach ($db as $connection_name => $db_values) {
		// first we must convert to dsn format
		$dsn = $db[$connection_name]['type'] .
			'://' . $db[$connection_name]['connection']['username'] .
			':' . $db[$connection_name]['connection']['password'].
			'@' . $db[$connection_name]['connection']['hostname'] .
 			'/' . $db[$connection_name]['connection']['database'];
 
		Doctrine_Manager::connection($dsn, $connection_name);
	}
 
	// telling Doctrine where our models are located
	if ( is_dir(APPPATH.'doctrine/models') )
		Doctrine::loadModels(APPPATH.'doctrine/models');
 
	// (OPTIONAL) CONFIGURATION BELOW
 
	// this will allow us to use "mutators"
	$manager->setAttribute(Doctrine_Core::ATTR_AUTO_ACCESSOR_OVERRIDE, true );
	// Automatically free queries
	$manager->setAttribute(Doctrine_Core::ATTR_AUTO_FREE_QUERY_OBJECTS, true );
	// Enable validation
	//$manager->setAttribute(Doctrine::ATTR_VALIDATE, Doctrine::VALIDATE_ALL);
	// this sets all table columns to notnull and unsigned (for ints) by default
	$manager->setAttribute(
	Doctrine_Core::ATTR_DEFAULT_COLUMN_OPTIONS,
		array('notnull' => true, 'unsigned' => true)
	);
	// set the default primary key to be named 'id', integer, 4 bytes
	$manager->setAttribute(
	Doctrine_Core::ATTR_DEFAULT_IDENTIFIER_OPTIONS,
		array('name' => 'id', 'type' => 'integer', 'length' => 4)
	);

The more astute of you out there will have noticed I made a reference to /doctrine/models for my Doctrine models directory. I kept the models directory outside of the doctrine module folder so that the module folder may be freely copied between installations without worrying about fussing with deleting models belonging to other websites, but it DOES mean you’ll need to create this folder on each site. Do so now:

/doctrine
/doctrine/models

You should now have a successful Doctrine installation. I’d quickly like to make two more additions though – firstly: fixtures. Fixtures come in two forms – data fixtures and schema files. Schema files with generate Doctrine models and data fixtures insert data into your database tables. If you take the time to learn how they work they’ll make your life alot easier. To add fixture support Create the following folders:

/doctrine/fixtures
/doctrine/fixtures/data
/doctrine/fixtures/schema
/modules/doctrine/classes/controller
/modules/doctrine/classes/controller/doctrine.php

Now to make the controller to load our fixtures. Here’s the code (to be placed in /modules/doctrine/classes/controller/doctrine.php):

<?php defined('SYSPATH') or die('No direct script access.'); 
 
class Controller_Doctrine extends Controller
{
	public function before()
	{
		parent::before(); 
 
		//Restrict controller to localhost
		if ( !in_array($_SERVER['REMOTE_ADDR'], array('::1', '127.0.0.1')) )
		{
			echo "DENIED!";
			exit;
		}
	}
 
	function action_load_fixtures()
	{
		?>
		This will delete all existing data!<br />
		<form action="" method="POST">
			<input type="submit" name="schema" value="Load Schema"><br /><br />
		<?php 
 
		?>
		This will delete all existing data!<br />
		<form action="" method="POST">
			<input type="submit" name="data" value="Load Fixtures"><br /><br />
		<?php
 
		if ( !empty($_POST['schema']) )
		{
			Doctrine_Core::generateModelsFromYaml(
				APPPATH . DIRECTORY_SEPARATOR . 'doctrine/fixtures/schema',
				APPPATH . DIRECTORY_SEPARATOR . 'doctrine/models',
				array('generateBaseClasses'=>false)
			);
			echo "Done!";
		}
		elseif ( !empty($_POST['data']) )
		{
			Doctrine_Manager::connection()->execute("
				SET FOREIGN_KEY_CHECKS = 0
			"); 
 
			Doctrine::loadData(APPPATH . DIRECTORY_SEPARATOR . 'doctrine/fixtures/data');
			echo "Done!";
		}
	}
}

But wait! There’s more!

As nice as automatically generating our models and entering data into our tables is, wouldn’t it be nice if we didn’t even have to create the tables in the database manually? We’ve already got a model file describing exactly what the tables structure should be – why can’t it also be done for us? Answer: It can! Add the following function to the doctrine controller:

function action_create_tables()
{
	?>
	Reminder: Make sure the tables do not exist already.<br />
	<form action="" method="POST">
	<input type="submit" name="action" value="Create Tables"><br /><br />
	<?php 
 
	if ( !empty($_POST['action']) )
	{
		Doctrine::createTablesFromModels();
		echo "Done!";
	}
}

All done. To generate data and schema fixtures and create database tables based on your models you can now use the following URLs:
Tables: http://mysite.com/doctrine/create_tables
Fixtures: http://mysite.com/doctrine/load_fixtures

  • jag.c

    Well done, i have to test it first though. I’m integrating Doctrine 1.2.3 and see if it works. Thanks alot!

    P.s.
    Btw, why does “latest build” link point to kohanaframework.org? shouldn’t it be on doctrine? nyways, Cheers!

    • Flynsarmy

      Oops, corrected.

  • Great

    Great tutorial bu you could add special function for db->models

  • hermit

    Excellent…thanks for doing the legwork to get this integrated.

    One small note, using Doctrine 1.2.3…
    /modules/doctrine/init.php:
    require Kohana::find_file(‘classes’, ‘doctrine/Doctrine’);
    …would not locate the Doctrine.php file.

    Changed this line to..
    require Kohana::find_file(‘classes’, ‘doctrine/lib/Doctrine’);

  • Theoadu

    Great work!
    I have followed your tutorial and even downloaded the download version of the modules but it is rather unfortunate i am experiencing this error i have done everything i can to resolve this issue all to no avail. Could you please offer me any assistance?

    I did exactly what is was in the tutorials but no results. I am using the latest version of Kohana with the download version of smarty from your site.

    ErrorException [ Fatal Error ]: Call to undefined method Kohana::config()
    MODPATH\doctrine\init.php [ 6 ]
    1 <?php
    2 /* Doctrine integration */
    3 require Kohana::find_file('classes', 'vendor/doctrine/Doctrine','php');
    4 spl_autoload_register(array('Doctrine', 'autoload'));
    5 // Getting kohana configurations for doctrine
    6 $db = Kohana::config('database');
    7 // initializing manager
    8 $Manager = Doctrine_Manager::getInstance();
    9
    10 // we load our database connections into Doctrine_Manager
    11 // this loop allows us to use multiple connections later on
    {PHP internal call} » Kohana_Core::shutdown_handler()
    Environment

    • Flynsarmy

      Hey Theo,

      This module was meant for Kohana 3.0 and may not work in 3.1. I’ll have to make a 3.1 version at some point but until that point you’ll need to find whatever 3.1′s equivalent of Kohana::config() is.

      • ramash

        Hi,
        Have you tried using doctrine gedmo extensions with yaml mapping in your module? (generating entities) my all attemps failed. I`m looking for the solution with no success yet.
        rm