7

Integrating Doctrine Into Kohana 3 [Updated 21 Feb 2011]

Posted (Updated ) 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