Integrating Doctrine 2 with CodeIgniter 2

If you’re looking for a quick way to get Doctrine 2 running with CodeIgniter 2, you might want to download my CodeIgniter 2/Doctrine 2 package

Overview

CodeIgniter is a great PHP framework. The codebase is clean, the documentation is fantastic, and it’s regularly updated. Doctrine is a good ORM for the same reasons: it’s very well-written, has extensive documentation, and is actively developed. A combination of these two systems makes it easy to build database-oriented web applications quicker than ever before.

To get started, download CodeIgniter 2 and Doctrine 2 – make sure you use the ‘Download Archive’ link when downloading Doctrine.

Setting up CodeIgniter

Put all of the CodeIgniter files into a directory on your web server, and configure it 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 1. Unfortunately one of the major changes to CodeIgniter 2 is the removal of plugins. EllisLab recommends you refactor your plugins as libraries, so that’s exactly what we will do.

Setting up Doctrine as a CodeIgniter library is fairly simple:

  1. Put the Doctrine directory into application/libraries (so that you have a application/libraries/Doctrine directory).
  2. Create the file application/libraries/Doctrine.php. This will be our Doctrine bootstrap as well as the library that CodeIgniter loads.
  3. Copy the following code into Doctrine.php:
<?php

use Doctrine\Common\ClassLoader,
    Doctrine\ORM\Tools\Setup,
    Doctrine\ORM\EntityManager;

class Doctrine
{
    public $em;

    public function __construct()
    {
        require_once __DIR__ . '/Doctrine/ORM/Tools/Setup.php';
        Setup::registerAutoloadDirectory(__DIR__);

        // Load the database configuration from CodeIgniter
        require APPPATH . 'config/database.php';

        $connection_options = array(
            'driver'        => 'pdo_mysql',
            'user'          => $db['default']['username'],
            'password'      => $db['default']['password'],
            'host'          => $db['default']['hostname'],
            'dbname'        => $db['default']['database'],
            'charset'       => $db['default']['char_set'],
            'driverOptions' => array(
                'charset'   => $db['default']['char_set'],
            ),
        );

        // With this configuration, your model files need to be in application/models/Entity
        // e.g. Creating a new Entity\User loads the class from application/models/Entity/User.php
        $models_namespace = 'Entity';
        $models_path = APPPATH . 'models';
        $proxies_dir = APPPATH . 'models/Proxies';
        $metadata_paths = array(APPPATH . 'models');

        // Set $dev_mode to TRUE to disable caching while you develop
        $config = Setup::createAnnotationMetadataConfiguration($metadata_paths, $dev_mode = true, $proxies_dir);
        $this->em = EntityManager::create($connection_options, $config);

        $loader = new ClassLoader($models_namespace, $models_path);
        $loader->register();
    }
}

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

Lines 34-37:

$models_namespace = 'Entity';
$models_path = APPPATH . 'models';
$proxies_dir = APPPATH . 'models/Proxies';
$metadata_paths = array(APPPATH . 'models');

These lines determine where you need to put your model files, and how you instantiate your model classes. With these settings, your models must live in application/models/Entity and be in the Entity namespace. For example instantiating a new Entity\User would load the User class from application/models/Entity/User.php

Line 40

$config = Setup::createAnnotationMetadataConfiguration($metadata_paths, $dev_mode = true, $proxies_dir);

This line uses Doctrine’s Setup class to automatically create the metadata configuration. The Metadata Driver is what Doctrine uses to interpret your models and map them to the database. In this tutorial I have used the Annotations Driver. If you would like to use a different metadata driver, change createAnnotationMetadataConfiguration to one of the methods below:

Notice the $dev_mode variable. This should be true while you are developing, as it does a couple of things:

  1. Automatically generates your proxy classes
  2. Uses ArrayCache; a non-persistent caching class

Likewise, when $dev_mode is false:

  1. Prevents your proxy classes from being overwritten
  2. Attempts to use one of the following persistent caches, in this order: APC, Xcache, Memcache (127.0.0.1:11211), and Redis (127.0.0.1:6379)

Advanced Configuration

For more advanced configuration, read the Configuration section of the Doctrine documentation.

Loading Doctrine

Doctrine can now be loaded in the same way as any other CodeIgniter library:

$this->load->library('doctrine');

Once the Doctrine library is loaded, you can retrieve the Entity Manage like so:

$em = $this->doctrine->em;

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 Entity;

/**
 * @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 create a table with the same name as the class.

Annotations that you will probably use frequently are:

  • @Column – Marks a property for object-relational persistence
  • @OneToOne@ManyToOne and @ManyToMany – Define a relationship between two entities
  • @JoinTable – Used when defining a @ManyToMany relationship to specify the database join table

For a full list of the available annotations and their uses, see the Annotation Reference

Below are two sample entities that show a basic one-to-many relationship:

<?php

namespace Entity;

use Doctrine\Common\Collections\ArrayCollection;

/**
 * @Entity
 * @Table(name="user")
 */
class User
{
    /**
     * @Id
     * @Column(type="integer", nullable=false)
     * @GeneratedValue(strategy="AUTO")
     */
    protected $id;

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

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

    /**
     * @Column(type="string", length=255, unique=true, nullable=false)
     */
    protected $email;

    /**
     * The @JoinColumn is not necessary in this example. When you do not specify
     * a @JoinColumn annotation, Doctrine will intelligently determine the join
     * column based on the entity class name and primary key.
     *
     * @ManyToOne(targetEntity="Group")
     * @JoinColumn(name="group_id", referencedColumnName="id")
     */
    protected $group;
}

/**
 * @Entity
 * @Table(name="group")
 */
class Group
{
    /**
     * @Id
     * @Column(type="integer", nullable=false)
     * @GeneratedValue(strategy="AUTO")
     */
    protected $id;

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

    /**
     * @OneToMany(targetEntity="User", mappedBy="group")
     */
    protected $users;
}

It is important to note that any mapped properties on your Entities need to be either private or protected, otherwise lazy-loading might not work as expected. This means that it is necessary to use Java-styled getter and setter 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:generate-entities command.

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.

All you need to do is create the file application/doctrine.php and copy the following code into it:

<?php

define('APPPATH', dirname(__FILE__) . '/');
define('BASEPATH', APPPATH . '/../system/');
define('ENVIRONMENT', 'development');

chdir(APPPATH);

require __DIR__ . '/libraries/Doctrine.php';

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

$doctrine = new Doctrine;
$em = $doctrine->em;

$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)
));

\Doctrine\ORM\Tools\Console\ConsoleRunner::run($helperSet);

On a Linux or Mac, you can now run ./application/doctrine from the command line.

If you are on Windows, you can run php.exe 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.

Using the Doctrine Console

If you run the Doctrine console with no arguments, you will be presented with a list of the available commands. For now, we are only interested in the orm:schema-tool commands. If you would like to learn about the other commands you can run a command with the --help flag or read my quick Doctrine console overview.

orm:schema-tool:create will create tables in your database based on your Doctrine models.

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. Any tables that aren’t mapped to a Doctrine model will be left alone.

orm:schema-tool:update will determine if your database schema is out-of-date, and give you the option of updating it. You can execute this command with the --dump-sql flag to see the changes, or the --force flag to execute the changes.

Using Doctrine

Once your models are set up and your database is built, you can access your models using the Doctrine EntityManager. I like shortcuts, so I always 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');

        $this->em = $this->doctrine->em;
    }
}

Instead of the longer $this->doctrine->em, this will allow you to access the EntityManager using $this->em:

$user = new Entity\User;
$user->setUsername('Joseph');
$user->setPassword('secretPassw0rd');
$user->setEmail('josephatwildlyinaccuratedotcom');

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

Final Words

If you have been spoiled 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!

Did you find this post useful?

  • Jance Kayre

    Hi
    Seriously, this tutorial is awesome! I’ve managed more than I did by myself.

    I just have a question : i got this error when I create a objet User :

    exception ‘DoctrineCommonPersistenceMappingMappingException’ with message ‘No mapping file found named ‘Entity.User.dcm.yml’ for class ‘EntityUser’.’ in /var/www/site/application/libraries/Doctrine/Common/Persistence/Mapping/MappingException.php:74

    But the thing is that my mapping files haven’t any “Entity.****”
    And I don ‘t understand why ?
    If someone could help me or show me the direction to follow

  • Guest

    Hi
    Seriously, this tutorial is exceptional ! I’ve managed more than I did by myself.

    I just have a question : i got this error when I create a objet User :

    exception ‘DoctrineCommonPersistenceMappingMappingException’ with message ‘No mapping file found named ‘Entity.User.dcm.yml’ for class ‘EntityUser’.’ in /var/www/site/application/libraries/Doctrine/Common/Persistence/Mapping/MappingException.php:74

    But the thing is that my mapping files haven’t any “Entity.****”
    And I don ‘t understand why ?
    If someone could help me or show me the direction to follow

  • suresh varman

    Excellent tutorial i love it thank you friend..

  • Inaamul Hassan

    Thank you frd..awsome tutorial

  • bavon mike

    Thanks so much…works fine

  • Vladimir Runchev

    I followed the tutorial exactly as you wrote. I am able to get the Entity Manager in my Codeigniter controllers.
    Here is the Doctrine.php:

    $models_namespace = ‘Entity’;
    $models_path = APPPATH . ‘models’;
    $proxies_dir = APPPATH . ‘models/Proxies’;
    $metadata_paths = array(APPPATH . ‘models/Entity’);
    $config = Setup::createAnnotationMetadataConfiguration($metadata_paths, $dev_mode = true, $proxies_dir);
    $this->em = EntityManager::create($connection_options, $config);

    I can read an Entity out of a database:

    //This is in the Test Controller

    $this->load->library(‘doctrine’);
    $em = $this->doctrine->em;

    $repo = $em->getRepository(‘EntityUser’);
    $user = $repo->findByEmail(‘vlatkorun@gmail.com’);

    If I do var_dump($user) I can see that the $user object is populated with data from the database. But if I try:
    echo $user->getEmail();

    I get an error:
    function getEmail() on a non-object

    Why is this error appearing? I am able to insert data in the Database also with creating a new EntityUser object and running $em->flush().

    I forgot to mention that Doctrine is not auto-generating/creating the Proxy classes at all. Maybe this is the source of the error because the $user needs to be a Proxy class?

    • pernelle sebastien

      i have the same problem , have you the solution ?

  • http://marcamon.fr/ Jean-Marc Amon

    Thanks so much for this

  • Miles

    Hey Joseph, thank you for your tuts, very instructive and usefull.
    Nevertheless, I can’t get it to work properly when willing to retrieve let’s say a user from the User table, whom model is stored in Entity/User.php. Can you help me sorting that out please ?
    I’m doing:

    $em = $this->doctrine->em;

    $row = $em->getRepository(‘Entity/User’)->findBy(array(‘email’ => $email));

    after loading the Doctrine Library but I have a Entity/User model doesn’t exist.

    Why ?

    Thanks

    • wildlyinaccurate

      Hi Miles,

      It’s hard to tell what’s going on without seeing your entire setup. From the info you’ve given me, I’d guess that the User model isn’t in the Entity namespace? If you’re still having trouble, try working from the sample setup I’ve got here: https://github.com/wildlyinaccurate/CodeIgniter-2-with-Doctrine-2

  • Sharaz Khan

    Would be also great if you could provide some example of doctrine queries with select , update , delete , joins etc..

  • Sharaz Khan

    Excellent tutorial.. Great work!!!

  • lilo

    Thanks, but in Doctrine.php, where is Line46 and Line 48?!

    • wildlyinaccurate

      Sorry, I had the line numbers wrong! I’ve updated them now.

      • lilo

        Ok thanks :)

  • http://sportannica.com/ Lance Newman

    I tried using “$this->load->library(‘doctrine’);

    $this->em = $this->doctrine->em;”

    and got the following. I don’t even see a folder in the Doctrine download called “common”

    A PHP Error was encountered

    Severity: Warning

    Message: require_once(/Applications/MAMP/htdocs/CodeIgniter/application/libraries/Doctrine/Common/ClassLoader.php): failed to open stream: No such file or directory

    Filename: Tools/Setup.php

    Line Number: 47

  • http://www.yaronguez.com Yaron Guez

    This tutorial is a life saver. Thanks so much! When I use the command line function orm:schema-tool:create I get an error Fatal error: Class ‘CI_Model’ not found in [..application path..]/models/demo_auth_admin_model.php on line 3. demo_auth_admin is a standard CodeIgniter model I’m already using separate from Doctrine. When I modify Doctrine.php and change the $metadata_paths to array(APPPATH . ‘models/Entity’) this problem goes away and the update seems to work fine. Is this the correct way to address this problem? Thanks again!

    • wildlyinaccurate

      Hey Yaron, the paths depend on where your entities are and which namespace they’re in, but if ‘models/Entity’ works for you then that’s fine! Glad you got it working.

      • http://www.yaronguez.com Yaron Guez

        Thanks for the reply! But I really think you should update the tutorial to reflect this fact. I followed the instructions exactly…to the letter, and received an error when running the command line tool to create the schema. If users create their entities within the model/Entity folder, like you suggest, and have any other native CodeIgniter models in the model folder, which they most likely will, they too will have this error. Since all of the entities are within the model/Entity folder then you should instruct Doctrine to use this folder, and not the parent model folder, as the path for metadata when creating the schema. Otherwise the command line tool will try to parse all of your native CodeIgniter models as well and will fail. Of course I could be mistaken and please correct me if I am. Thanks again for your help!

        • wildlyinaccurate

          Ah yes, I see what you mean now – Doctrine was trying to load metadata from your non-Doctrine models, because we’ve specified the metadata path as APPPATH . 'models' instead of APPPATH . 'models/Entity'.

          I’ll update the tutorial after I’ve tested this on a few of my projects. Thanks for taking the time to explain it!

  • Dennis van Meel

    I just installed your package in Netbeans and changed the db information in my config/database.php file. Tried to persist the data but I just get an internal server error (500). I run a localhost server on MAMP and didn’t change a thing in the code. Could you help me out? The tutorial was very good, and I think the most up-to-date one that can be found on google. Great Job!

  • Pingback: Can#39;t find entity model in Doctrine 2 / CodeIgniter 2 | Code and Programming

  • http://www.facebook.com/wesley.murch Wes Murch

    I believe you have an error in your tutorial:

    $user = new modelsUser;

    Should be:

    $user = new EntityUser;

  • piter lelaona

    I find the concept hmvc here https://github.com/rubensarrio/codeigniter-hmvc-doctrine
    but still using version 2.1 of doctrine, I tried to upgrade to doctrine2.3 version but found a lot of errors. how to fix it … thank you very much. I really appreciate it..
    sory about my english.. i am from indonesia.. :)