Integrating Doctrine 2 with CodeIgniter 2

Overview

CodeIgniter is my favourite PHP framework. The documentation is fantastic, and there is a wide range of community-developed libraries available. Recently, however, CodeIgniter has received criticism from users of other frameworks for its lack of updates and non-modular design.

Doctrine is my ORM of choice but like CodeIgniter, it has been criticised for its overly-magic approach and poor performance.

CodeIgniter and Doctrine have both received a complete overhaul, and version 2 of each promises huge benefits over the previous versions. CodeIgniter 2 focuses on modularity and the performance gains of PHP5. Doctrine 2 loses most of its magic, but receives dramatic performance gains by making good use of PHP5’s OOP features.

When I heard that CodeIgniter 2 and Doctrine 2 were both in beta, I took the opportunity to have a sneak-peak at both systems. This post documents my approach to integrating Doctrine 2 with CodeIgniter 2, and acts as a guide for others that wish to do the same. If you are following this as a guide, you can start by downloading Doctrine 2 and CodeIgniter 2.

Setting up CodeIgniter

Put all of the CodeIgniter files into a directory of your choice on your web server. I like to keep things simple and put CodeIgniter into the root directory. If you’re a particularly complicated person you may want to put CodeIgniter deep inside a maze of sub-directories – that’s your choice, and I won’t judge you for it.

At this point you might like to configure CodeIgniter to your liking. If you need a guide on how to do this, the best place to look is the CodeIgniter User Guide. Keep in mind that Doctrine will load the database configuration from CodeIgniter (application/config/database.php).

Setting up Doctrine

Because Doctrine is an entire system in itself, it worked well as a plugin for CodeIgniter. Unfortunately one of the major changes to CodeIgniter 2 is the removal of plugins. EllisLab recommends you refactor your plugins as libraries, or even helpers. So that’s exactly what we will do.

Setting up Doctrine as a CodeIgniter library is fairly simple:

  • Put the Doctrine folder into application/libraries. (Fantastic, we’re halfway there!)
  • Create a file called Doctrine.php in application/libraries. This will be the bootstrap file for Doctrine’s entity manager.
  • Copy the following code into Doctrine.php:
<?php

use Doctrine\Common\ClassLoader,
    Doctrine\ORM\Configuration,
    Doctrine\ORM\EntityManager,
    Doctrine\Common\Cache\ArrayCache,
    Doctrine\DBAL\Logging\EchoSqlLogger;

class Doctrine {

  public $em = null;

  public function __construct()
  {
    // load database configuration from CodeIgniter
    require_once APPPATH.'config/database.php';
    // Set up class loading. You could use different autoloaders, provided by your favorite framework,
    // if you want to.
    require_once APPPATH.'libraries/Doctrine/Common/ClassLoader.php';

    $doctrineClassLoader = new ClassLoader('Doctrine',  APPPATH.'libraries');
    $doctrineClassLoader->register();
    $entitiesClassLoader = new ClassLoader('models', rtrim(APPPATH, '/'));
    $entitiesClassLoader->register();
    $proxiesClassLoader = new ClassLoader('Proxies', APPPATH.'models/proxies');
    $proxiesClassLoader->register();

    // Set up caches
    $config = new Configuration;
    $cache = new ArrayCache;
    $config->setMetadataCacheImpl($cache);
    $config->setQueryCacheImpl($cache);

    // Set up driver
    $Doctrine_AnnotationReader = new \Doctrine\Common\Annotations\AnnotationReader($cache);
	$Doctrine_AnnotationReader->setDefaultAnnotationNamespace('Doctrine\ORM\Mapping\\');
    $driver = new \Doctrine\ORM\Mapping\Driver\AnnotationDriver($Doctrine_AnnotationReader, APPPATH.'models');
    $config->setMetadataDriverImpl($driver);

    // Proxy configuration
    $config->setProxyDir(APPPATH.'/models/proxies');
    $config->setProxyNamespace('Proxies');

    // Set up logger
    //$logger = new EchoSqlLogger;
    //$config->setSqlLogger($logger);

    $config->setAutoGenerateProxyClasses( TRUE );

    // Database connection information
    $connectionOptions = array(
        'driver' => 'pdo_mysql',
        'user' =>     $db['default']['username'],
        'password' => $db['default']['password'],
        'host' =>     $db['default']['hostname'],
        'dbname' =>   $db['default']['database']
    );

    // Create EntityManager
    $this->em = EntityManager::create($connectionOptions, $config);
  }
}

There are some parts of this file that you might like to modify to suit your needs:

Line 23

$entitiesClassLoader = new ClassLoader('models', rtrim(APPPATH, '/'));

This line tells Doctrine where to look when we ask it to load a class in the models namespace. It is important that you understand how Doctrine’s ClassLoader behaves: from looking at this line you might guess that if you load the models\User class, Doctrine will look for application/User.php. It doesn’t. It looks for application/models/User.php.

I spent a lot of time trying to find ways around this behaviour, but it seems that this is the only way to have Doctrine play nicely with CodeIgniter (short of loading the classes manually). The only drawback to this is that you need to instantiate a new models\User, as the ClassLoader will only look for classes in the namespace that you provide.

Lines 35-37

$Doctrine_AnnotationReader = new \Doctrine\Common\Annotations\AnnotationReader($cache);
$Doctrine_AnnotationReader->setDefaultAnnotationNamespace('Doctrine\ORM\Mapping\\');
$driver = new \Doctrine\ORM\Mapping\Driver\AnnotationDriver($Doctrine_AnnotationReader, APPPATH.'models');

These lines define which metadata (mapping) driver Doctrine will use to interpret your models. In this example I have used the AnnotationReader driver, which allows you to use docblock annotations to define your models. I am a big fan of docblock annotations because I like to have my metadata in the same place as my class properties. You may be more comfortable using the YAML or XML metadata drivers.

Doctrine can now be loaded by CodeIgniter, by calling $this->load->library(‘doctrine’); or by adding ‘doctrine’ to the $autoload[‘libraries’] array in application/config/autoload.php.

Defining Models

Building models using the AnnotationDriver is simple. You can build your classes as if they were regular PHP classes, and define the Doctrine metadata in Docblock annotations.

<?php

namespace models;

/**
 * @Entity
 * @Table(name="user")
 */
class User {

}

The @Entity annotation marks this class for object-relational persistence. If the table name is not specified (using the @Table annotation), Doctrine will persist the entity to a table with the same name as the class.

Similarly, the @Column annotation can be used to mark a property for object-relational persistence. Common annotations include @Id, to mark a property as a primary key; @GeneratedValue, to define the value generation strategy (must be used in conjunction with @Id); and @JoinColumn, which along with @OneToOne or @ManyToOne defines relationships between entities. For a list of all the possible annotations, see Doctrine’s Annotation Reference.

	/**
	 * @Id
	 * @Column(type="integer", nullable=false)
	 * @GeneratedValue(strategy="AUTO")
	 */
	private $id;

	/**
	 * @Column(type="string", length=32, unique=true, nullable=false)
	 */
	private $username;

	/**
	 * @Column(type="string", length=64, nullable=false)
	 */
	private $password;

	/**
	 * @Column(type="integer", nullable=false)
	 * @ManyToOne(targetEntity="Groups")
	 * @JoinColumn(name="user_group", referencedColumnName="id")
	 */
	private $user_group;

Since Doctrine 2 is strictly object-oriented, it is a good idea to use object-oriented best-practices. One of these best-practices is making all the properties in your class private or protected. Because the class is now essentially encapsulated, we need to use Java-styled set() and get() public methods:

	public function setUsername($username)
	{
		$this->username = $username;
	}

	public function getUsername()
	{
		return $this->username;
	}

Thankfully you can save a lot of time and automatically generate these methods using the orm:convert-mapping command with the –from-database flag. Alternatively, you can use my magic get/set method which utilises PHP’s __call() method. While the magic method is not best-practice, it can reduce the size of your classes and still allow you to override set and get methods for individual properties.

Setting up the Doctrine Console

This step is optional, however the Doctrine Console has some very useful commands so I highly recommend setting it up.

First, create the file application/cli-config.php and copy this code into it:

<?php
use Doctrine\Common\ClassLoader,
    Doctrine\ORM\Configuration,
    Doctrine\ORM\EntityManager,
    Doctrine\Common\Cache\ArrayCache,
    Doctrine\DBAL\Logging\EchoSqlLogger;

define('BASEPATH', __DIR__ . '/../system/');

require_once __DIR__ . '/config/database.php';
require_once __DIR__ . '/libraries/Doctrine/Common/ClassLoader.php';

$doctrineClassLoader = new ClassLoader('Doctrine',  __DIR__.'libraries');
$doctrineClassLoader->register();
$entitiesClassLoader = new ClassLoader('models', __DIR__);
$entitiesClassLoader->register();
$proxiesClassLoader = new ClassLoader('Proxies', __DIR__.'models/proxies');
$proxiesClassLoader->register();
$config = new \Doctrine\ORM\Configuration();
$config->setMetadataCacheImpl(new \Doctrine\Common\Cache\ArrayCache);
$config->setProxyDir(__DIR__ . '/Proxies');
$config->setProxyNamespace('Proxies');

$cache = new ArrayCache;
// Set up driver
$Doctrine_AnnotationReader = new \Doctrine\Common\Annotations\AnnotationReader($cache);
$Doctrine_AnnotationReader->setDefaultAnnotationNamespace('Doctrine\ORM\Mapping\\');
$driver = new \Doctrine\ORM\Mapping\Driver\AnnotationDriver($Doctrine_AnnotationReader, realpath('../models'));
$config->setMetadataDriverImpl($driver);

// Database connection information
$connectionOptions = array(
    'driver' => 'pdo_mysql',
    'user' =>     $db['default']['username'],
    'password' => $db['default']['password'],
    'host' =>     $db['default']['hostname'],
    'dbname' =>   $db['default']['database']
);

$em = \Doctrine\ORM\EntityManager::create($connectionOptions, $config);

$helperSet = new \Symfony\Component\Console\Helper\HelperSet(array(
    'db' => new \Doctrine\DBAL\Tools\Console\Helper\ConnectionHelper($em->getConnection()),
    'em' => new \Doctrine\ORM\Tools\Console\Helper\EntityManagerHelper($em)
));

At this point you should configure this in the same way that you configured application/libraries/Doctrine.php – make sure the ClassLoader paths and AnnotationDriver are the same.

Now create the file application/doctrine.php and copy the following code into it:

<?php
chdir(dirname(__FILE__) . '/libraries');

require_once 'Doctrine/Common/ClassLoader.php';

$classLoader = new \Doctrine\Common\ClassLoader('Doctrine');
$classLoader->register();

$classLoader = new \Doctrine\Common\ClassLoader('Symfony', 'Doctrine');
$classLoader->register();

$configFile = '../cli-config.php';
$helperSet = null;
if (file_exists($configFile)) {
    if ( ! is_readable($configFile)) {
        trigger_error(
            'Configuration file [' . $configFile . '] does not have read permission.', E_ERROR
        );
    }

    require $configFile;

    foreach ($GLOBALS as $helperSetCandidate) {
        if ($helperSetCandidate instanceof \Symfony\Component\Console\Helper\HelperSet) {
            $helperSet = $helperSetCandidate;
            break;
        }
    }
}

$helperSet = ($helperSet) ?: new \Symfony\Component\Console\Helper\HelperSet();

$cli = new \Symfony\Component\Console\Application('Doctrine Command Line Interface', Doctrine\ORM\Version::VERSION);
$cli->setCatchExceptions(true);
$cli->setHelperSet($helperSet);
$cli->addCommands(array(
    // DBAL Commands
    new \Doctrine\DBAL\Tools\Console\Command\RunSqlCommand(),
    new \Doctrine\DBAL\Tools\Console\Command\ImportCommand(),

    // ORM Commands
    new \Doctrine\ORM\Tools\Console\Command\ClearCache\MetadataCommand(),
    new \Doctrine\ORM\Tools\Console\Command\ClearCache\ResultCommand(),
    new \Doctrine\ORM\Tools\Console\Command\ClearCache\QueryCommand(),
    new \Doctrine\ORM\Tools\Console\Command\SchemaTool\CreateCommand(),
    new \Doctrine\ORM\Tools\Console\Command\SchemaTool\UpdateCommand(),
    new \Doctrine\ORM\Tools\Console\Command\SchemaTool\DropCommand(),
    new \Doctrine\ORM\Tools\Console\Command\EnsureProductionSettingsCommand(),
    new \Doctrine\ORM\Tools\Console\Command\ConvertDoctrine1SchemaCommand(),
    new \Doctrine\ORM\Tools\Console\Command\GenerateRepositoriesCommand(),
    new \Doctrine\ORM\Tools\Console\Command\GenerateEntitiesCommand(),
    new \Doctrine\ORM\Tools\Console\Command\GenerateProxiesCommand(),
    new \Doctrine\ORM\Tools\Console\Command\ConvertMappingCommand(),
    new \Doctrine\ORM\Tools\Console\Command\RunDqlCommand(),
    new \Doctrine\ORM\Tools\Console\Command\ValidateSchemaCommand(),

));
$cli->run();

If you are on Windows, you can now run application/doctrine.php from the command line. If you are new to running PHP scripts from the command line in Windows, you may want to read PHP’s documentation.

If you are on a Unix/Linux system, you are blessed with a much smarter command line interface and can create the file application/doctrine and copy the following into it:

#!/usr/bin/env php
<?php

include('doctrine.php');

You can now run application/doctrine from the command line.

Using the Doctrine Console

If you run the script we just created with no arguments, you will be presented with a list of the available commands. For now, we are only interested in orm:schema-tool:drop and orm:schema-tool:create. If you would like to learn about the other commands you can run a command with the –help flag, or read Doctrine’s Documentation (I recommend learning about orm:convert-mapping and orm:generate-entities).

orm:schema-tool:create will create tables in your database based on your Doctrine models. Simply run this command with no arguments and let Doctrine handle the rest.

orm:schema-tool:drop will do the exact opposite. It is worth noting that this command will only drop tables which have correlating Doctrine models.

See my post on creating custom Doctrine Console commands to learn how to load data from fixtures.

Using Doctrine

Once your models are set up and your database is built, you can access your models in the usual way. However, to do anything interesting with your models you first need to load up the Doctrine EntityManager. I prefer to instantiate the EntityManager in MY_Controller:

<?php
class MY_Controller extends Controller
{

	//Doctrine EntityManager
	public $em;

	function __construct()
	{
 		parent::__construct();

  		//Not required if you autoload the library
 		$this->load->library('doctrine');

  		//Instantiate a Doctrine Entity Manager
 		$this->em = $this->doctrine->em;
 	}
}

Edit: This step is completely optional. You can just access the EntityManager using $this->doctrine->em, but if you’re anything like me you want to minimize the amount of code you have to write to get things done so $this->em is simply a convenient shortcut.

You can now use $this->em inside any controller to access the EntityManager:

$user = new models\User;
$user->setUsername('Eryr');
$user->setPassword('secretPassw0rd');
$user->setEmail('joewynndotnz@gmaildotcom');

$this->em->persist($user);
$this->em->flush();

Note that you have to specify the namespace of the model you want to use so that Doctrine can automatically load the class. As I mentioned before, I couldn’t find a way around this behaviour. (If you know how to trick the ClassLoader so that we don’t need to specify the namespace of a class to load it automatically, please leave a comment!)

Final Words

If you have been spoiled rotten by Doctrine 1’s magic then you will probably have a hard time wrapping your head around Doctrine 2’s strict object-oriented approach to everything. When learning your way around a new system, documentation can be your best friend or your worst enemy. Lucky for us, the Doctrine team has written some fantastic documentation. If you are having trouble getting something to work, one of the first things you should do is RTFM (Doctrine; CodeIgniter).

Good luck, I hope you enjoy using these powerful tools!

Advertisements

, , ,

  1. #1 by Drew Spencer on October 2, 2010 - 3:08 am

    Thank You! What a legend you are – I have been trying to do this for days and could not find any decent guide. This really is excellent.

    I have one question though – I can’t seem to get MY_Controller.php to load. I can only access the EntityManager if I include $this->em = $this->doctrine->em; in the controller.

    I am just playing around atm with a test controller & model, but I would like to know where I’m going wrong.

    I have your My_Controller.php file in my application/libraries folder. I tried adding it to the autoload libraries array but that gives an error. Should it just load automatically?

    Regards,

    Drew

  2. #2 by Drew Spencer on October 2, 2010 - 3:54 am

    Scratch that – worked it out. Didn’t realise it has to be in application/core instead of application/libraries. Yay, I can save a user to my database!! I have a long way to go!

    I have another question though… how do you create the tables in the db from the class definitions? Is this dome using the CLI, in the part of the tutorial you haven’t completed yet? I previously used the Doctrine::createTablesFromModels(); function in 1.2, but it seems this is gone now?

    Many thanks,

    Drew

    • #3 by eryr on October 3, 2010 - 6:47 pm

      Hi Drew, glad to hear you worked it out!

      I created my tables using the CLI. I apologise for neglecting to finish that part of the tutorial – I’ll work on it tonight. Doctrine 2 actually comes with a bootstrap file for the CLI in the bin directory. I recall having to modify it slightly to get it working properly, but if you don’t want to wait for me to finish the tutorial you could have a play with it.

      • #4 by Drew Spencer on October 6, 2010 - 12:00 am

        Hey, no worries, and thanks for finishing the tutorial. I Have the CLI “working” now but I only ever get the same thing back from it – the list of functions that comes up when you just load type ‘doctrine’, when I type a command after it seems it is just ignored. I have been scouring through the doctrine.php and cli-config.php but I can’t seem to work it out. I didn’t make any changes to the code you gave – they are exactly as your examples are laid out.

        So the CLI is loading but can’t seem to load any of the actual functions. I don’t get any error messages or anything. My system and application folders are both in the codeigniter folder, instead of application being inside system (this is how I was set up using 1.7.2 + 1.2). Also I removed the index.php from the url and am using mod_rewrite in an htaccess file (Pretty standard). I tried changing these back to see if they were the issue but still had the same problem. I just can’t work out what I’ve done wrong! Sorry for bugging you again, but I spent the entire day yesterday trying to work this out. Thanks again if you can help. Otherwise I will keep sifting through the docs and examples and post on the forums. :S

      • #5 by Joseph on October 6, 2010 - 7:03 am

        If you can see the list of commands then everything should be working fine. Which command(s) are you running?

        I found that sometimes the console appeared to be doing nothing when actually it had run the command successfully. Are you able to run non-functional commands like doctrine list, and doctrine help?

  3. #6 by Kenny on October 4, 2010 - 3:22 am

    Thank you so much for this write up!

  4. #7 by Edmund on October 5, 2010 - 9:14 pm

    I’m relatively new to Doctrine, and I’m curious.. What directories do the different files go into using your example.

    I have most of this going, and can run the command line tools, but I get a blank page when I try doing anything in my controller.

    Also, aren’t your “models” really mapping files? Are we supposed to run “doctrine orm:generate-entities ” and use the Entities ?

    Thanks for any info on this.

  5. #8 by Edmund on October 5, 2010 - 9:58 pm

    A couple more questions, are we still supposed to autoload or load->model the “models” in CodeIgniter or no, we just use it like you would normally use Doctrine?

    I have a question about the following parts from your blog:
    —————————————————————————
    Where you are talking about creating the model and you talk about getters and setters, you say:
    “Thankfully you can save a lot of time and automatically generate these methods using the orm:convert-mapping command.”
    —-
    Isn’t this incorrect? That’s for converting between, Annotation, XML, and Yaml isn’t it? Isn’t the command you are talking about the “doctrine orm:generate-entities” command?

    If so, and I do run the “doctrine orm:generate-entities” command, it creates files that look more like a typical class file with the mapping info removed and with the getters and setters generated.

    Are these “Entities” that we should be using in CodeIgniter by autoloading or load->model?
    ————–

    I think one thing that is confusing me are these parts from your “Doctrine.php” file where it looks like they are reading from the same directory. But when I do this, and I don’t have a mapping file, I get a error. Are you generating Entities or no, your mapping file is your Entity?

    ==========================================================
    $entitiesClassLoader = new ClassLoader(‘models’, rtrim(APPPATH, ‘/’));

    $driver = new \Doctrine\ORM\Mapping\Driver\AnnotationDriver($Doctrine_AnnotationReader, APPPATH.’models’);
    $config->setMetadataDriverImpl($driver);
    =========================================================

    • #9 by Joseph on October 6, 2010 - 7:18 am

      1. Which files do you mean exactly?

      2. The blank page could be appearing for a number of reasons. If you are trying to create a Doctrine entity in that controller, use print_r() to check it has been created.

      3. You’re absolutely right – the ‘models’ are mapping files. They are also the Doctrine entities. That is the reason I use the AnnotationReader – so I can store the mapping data in the same file as the entity. If you are using YAML or XML for your mapping then you will need to create separate entity files similar to the ones outlined in this post, just without all the docblock comments.

      4. That’s correct. Thanks for pointing that out! You don’t need to load any of the models – Doctrine’s ClassLoader should take care of that for you. CodeIgniter doesn’t even need to know that these models exist.

      5. As I said in 3. the files you create in application/models are both the entities (PHP classes) and mapping files (docblock annotations). There is no need to generate the entities using the Doctrine console unless -like you pointed out- you want Doctrine to create all of the get/set methods.

      Have a play with some of the console commands like generate-entities so you can see exactly what they do. Make sure you set the output directory to somewhere that won’t override the models you created.

    • #10 by Joseph on October 12, 2010 - 9:28 pm

      Hi Edmun, I was updating my CI2/D2 application today and I remembered that you asked whether I was incorrect in saying “Thankfully you can save a lot of time and automatically generate these methods using the orm:convert-mapping command.”

      I apologise for the confusion but in fact I was not correct. The convert-mapping command has a –from-database flag which will generate your entity files based on the tables already defined in the database. An example of this command is: doctrine orm:convert-mapping --from-database annotation ../models which will generate your entities in the application/models directory.

      I will update the blog post to reflect this.

  6. #11 by Edmund on October 6, 2010 - 3:41 pm

    Hi,

    To follow up, actually, what is happening, is everything seems good up to a point. I can instantiate an object and call the functions. But when I do “”$this-em->persist($user);”, it stops. So, I’m not sure what’s going on there.

    I can var_dump or print_r() the entity manager, and I see things in there and there are no errors with “$this->em”, so, I’m kind of stumped as to what’s going on.

    Any ideas?

    Thanks for the great info and help.

    • #12 by Joseph on October 6, 2010 - 4:41 pm

      Alright, so you can print_r($user) and everything looks okay there? Do you get any errors when you call persist()?

      I would double-check that your table structure for User matches up with the mapping data. It might pay to generate your tables using the Doctrine console (orm:schema-tool:create) just to be sure that everything will match up.

      Other than that, I’m not sure what the problem could be. Maybe you could post the snippet of code where you create/persist $user.

      • #13 by Edmund on October 10, 2010 - 4:43 pm

        Hi Joseph,

        Not sure what happened with my other replies showing up in the other thread, but I traced things down.

        Where things are failing is in the “Doctrine/ORM/Mapping/Driver/AnnotationDriver.php”.

        I’m not sure what this is doing, but it is throwing an exception here:
        =======================
        // Evaluate Entity annotation
        if (isset($classAnnotations[‘Doctrine\ORM\Mapping\Entity’])) {
        $entityAnnot = $classAnnotations[‘Doctrine\ORM\Mapping\Entity’];
        $metadata->setCustomRepositoryClass($entityAnnot->repositoryClass);
        } else if (isset($classAnnotations[‘Doctrine\ORM\Mapping\MappedSuperclass’])) {
        $metadata->isMappedSuperclass = true;
        } else {

        // ** Here it throws an exception

        throw MappingException::classIsNotAValidEntityOrMappedSuperClass($className);
        }

        ============================
        Any idea what that is looking for?

        The schema validation is ok, the DB is created correctly and validates correctly after it is created too.

        I’m stumped. BTW, I downloaded the Doctrine source directly, I didn’t use SVN or GIT, could this make a difference? Although, I believe I have the directories all set up correctly.

        I wonder if I should start everything with fresh install of CI2 and D2 all over again..

        Thanks in advance for any help or info.

    • #14 by Edmund on October 10, 2010 - 4:54 pm

      Oh, one thing I am doing differently, is where you have the mapping and entity all in one file, I am trying to do mine with the mapping file, and generating the entities with the CLI tool.

      And I am doing this in Doctrine.php:
      —————————
      $entitiesClassLoader = new ClassLoader(‘models’, rtrim(APPPATH, ‘/’));
      $entitiesClassLoader->register();

      $driverImpl = $config->newDefaultAnnotationDriver(APPPATH.’models/mappings’); $config->setMetadataDriverImpl($driverImpl)
      ————————

      Where models is getting the Entity that was generated and the Annotation Driver is still reading the original mapping file that I created the Entity with.

      This is how that should work correct?

      If I do the mapping and the entity in the same file like you do, do I generate the entity with the CLI then move the mapping info into the generated entity file?

      This may be where the problem is, but I was under the impression that is how it should work if I have the mapping and entities in separate files.

      • #15 by Edmund on October 10, 2010 - 5:37 pm

        Ok Joseph,

        I went back to testing with your original User.php with the mapping and entity all in one. It works fine!

        What could be wrong when separating them?

        Like I said, I am telling the “models” to look in the “models/” dir and read the entity file that was generated from the cli tool.

        I was telling the annotation driver to to look at the original mapping files in the “models/mappings” dir that I generated my entity file with.

        Should it not be reading the original mapping file?

        I’m very close to solving this I believe.

      • #16 by Joseph on October 10, 2010 - 6:43 pm

        Hi Edmund,

        To be honest, if you’re using the annotation driver it makes more sense to have the metadata (annotations) in the same file as the entities. This is because the metadata basically provides the documentation that you should have in your class/entity anyway.

        The way you’ve set it up looks like it should work, but since I haven’t set mine up like that I’m not sure why yours wont work. Have you read the Doctrine documentation on how to use the XML mapping? It might be helpful because XML mapping is stored in different files from the entities – http://www.doctrine-project.org/projects/orm/2.0/docs/reference/xml-mapping/en#xml-mapping

      • #17 by Joseph on October 11, 2010 - 10:48 am

        Edmund, I’ve just had a thought – make sure the mapping files are in the same namespace as the entities.

  7. #18 by Drew Spencer on October 6, 2010 - 11:13 pm

    Hi Joseph, and thanks again for replying. I did originally think that the commands might be running, but it seems not. I have tried to drop the table that matched my User class (that i made in phpmyadmin), no luck there. Can’t create it either, and have also tried a couple of others, doctrine list and doctrine help don’t work either. Do you think it could be something outside of the doctrine/CI setup? I added my php.exe to the PATH, added .php to the pathext, and ran assoc .php=phpfile and ftype phpfile=”C:\PHP5\php.exe” -f “%1” — %~2. I’m starting to lose the will to live.

    • #19 by Edmund on October 7, 2010 - 9:47 am

      Yes, I do generate the tables with the cli tools and the “*:create” and I have even validated the schema and everything comes back with OK. I even messed things up to make sure it’s working and it is.

      I can print_r the object and I can see it. And no, unfortunately, there is no error coming back from the $this->em->persist() call. I can print_r the $this->em and it’s there.. So, I’m just totally stumped at this point.

      Thanks for the help and I will keep checking back to see what else is going on here. Your tutorial is great help, and I’ve gotten further with it then any other info I’ve found so far.

      • #20 by Edmund on October 7, 2010 - 9:54 am

        Oh. I do have another question if you have an answer.

        In the cli-config.php and Doctrine.php files, you have the return from the ClassLoader calls go into different Vars.

        $doctrineClassLoader, $entitiesClassLoader, and $proxiesClassLoader.

        I have seen other cli-configs where they are all assigned to “$classLoader”. Is there any particular reason that you have all separate vars and some have all one var.

        Also, if I have an extension to load, like the nested sets extension, should I assign the ClassLoader to it’s own var like “$doctrineExtClassLoader” or should it go to “$doctrineClassLoader” or what? I don’t quite understand why I see this shown differently in different examples.

        Thanks once again for your time, help and info.

      • #21 by Joseph on October 7, 2010 - 10:50 am

        That’s very odd… I still don’t understand what you mean when you say it just “stops” though, so I’m having trouble figuring out what the problem might be. You are remembering to call flush() when you have finished persisting the entities?

        $this->em->persist($user);
        $this->em->flush();
        

        I chose to assign the ClassLoaders to different variables to make the code a little more readable and also to avoid mistakes. You’ll notice in application/doctrine.php I called both of the ClassLoaders $classloader, which still produces the same result because each $classloader is registered before it is overwritten with the new ClassLoader.

        What wouldn’t produce the same result is if you tried to do:

        $classloader = new ClassLoader('abc');
        $classloader = new ClassLoader('xyz');
        
        $classloader->register();
        $classloader->register();
        

        Only xyz would be loaded. That is why assigning them to different variables is a bit safer.

        Hopefully that answers your question about extensions – it doesn’t matter what you call the variable when you register the ClassLoader

  8. #22 by Drew Spencer on October 7, 2010 - 4:22 am

    Got it working!! It was nothing to do with your code. I think I stupidly added the php path entry into the account one instead of the system one. Oh well we live and learn! Thanks again for such a great tutorial.

    • #23 by Joseph on October 7, 2010 - 8:28 am

      Ah yeah I had a bit of trouble with that too! Glad to hear you worked it out though.

      When you’ve had a good play with CI2/D2 why don’t you stop by and let me know what you think of them both? So far I’m finding the things that made D1 so attractive are missing from D2… Namely the magic. But if that’s the sacrifice I have to make in order to produce good quality code then I suppose I have no problem with that 🙂

  9. #24 by Drew Spencer on October 9, 2010 - 2:14 am

    Hi again. Still getting to grips with this, as you might imagine! Couple of questions

    1) When I run orm:ensure-production-settings I get the following message: “Query cache is not configured” Is this right or should I be doing something about it? I’m guessing it won’t make a difference until i’m doing some real database work, but just making sure it’s not a problem at this stage.

    2) When I run orm:generate-entities I get told to specify a dest-path. So I make the command “php doctrine.php orm:generate-entities=’true’ models” and I get “Fatal error: cannot redeclare class models/User in c:\xampp\htdocs\codeitniter\application\models\user.php on line 8”. What am I doing wrong here. Have tried changing the dest-path to various things but always the same.

    Thanks if you can help. And I will definitely give you some feedback on the new doctrine when I feel I am up to any kind of decent level :S

  10. #25 by bryant on October 29, 2010 - 9:33 am

    When I download the doctrine2 package, it does not have the Doctrine/Common directory, only the ORM directory…am i downloading the wrong package?

    • #26 by Joseph on October 29, 2010 - 11:18 am

      The Doctrine team seems to have changed the download page since I wrote this. Go to http://www.doctrine-project.org/projects/orm/download and grab the latest version (currently 2.0.0BETA4).

      You’ll end up with a gzipped tarball. When you extract everything (using 7-zip or similar) you should have 2 main directories: bin and Doctrine. Inside Doctrine you should find the Common, DBAL, ORM, and Symfony directories.

      Hope that helps!

  1. Custom Doctrine 2 Console Commands « Wildly Inaccurate

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google+ photo

You are commenting using your Google+ account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

%d bloggers like this: