Development

Doctrine With CodeIgniter: DB Modeling In CodeIngiter Like A Pro

Are you tired of CodeIgniter’s native database handling and craving the power of a robust ORM? Look no further! This comprehensive guide will show you exactly how to integrate Doctrine with CodeIgniter for a supercharged development experience.

Why Use Doctrine With CodeIgniter?

While CodeIgniter offers a decent Active Record pattern implementation for database operations, many developers (myself included) prefer working with a full-featured ORM. The combination of CodeIgniter’s lightweight framework and Doctrine’s powerful ORM capabilities creates an absolutely fantastic development environment.

I’ve previously covered CodeIgniter basics, but today I’m diving into something that will completely transform your CodeIgniter development experience. Doctrine integration solves one of CodeIgniter’s biggest limitations: the lack of a built-in ORM system.

What You’ll Learn in This Guide

  • How to install and configure Doctrine with CodeIgniter
  • Creating a custom library to handle the integration
  • Generating entity classes from your database automatically
  • Practical examples of using Doctrine within your CodeIgniter applications
  • Setting up the command-line tools for advanced operations

Let’s get started with this game-changing integration!

Prerequisites

Before diving in, you should have:

Installation and Setup

Step 1: Download and Install Doctrine

First, you need to get your hands on the latest Doctrine ORM package. You have two options:

Option 1: Direct Download

  1. Download the latest Doctrine ORM from the official website
  2. Extract the contents
  3. Copy the extracted ‘doctrine’ directory to your CodeIgniter application’s ‘third_party’ directory

Option 2: Using Composer (Recommended) If you’re already using Composer with CodeIgniter (which is a fantastic practice), simply add Doctrine as a dependency in your composer.json file:

"require": {
    "doctrine/orm": "^2.14"
}Code language: JavaScript (javascript)

Then run composer update to install Doctrine and its dependencies.

Step 2: Create the Doctrine Library

Now, let’s create a custom library to handle the Doctrine initialization. Create a new file named ‘Doctrine.php’ in your ‘application/libraries’ directory:

<?php

use Doctrine\Common\ClassLoader,
    Doctrine\ORM\Configuration,
    Doctrine\ORM\EntityManager,
    Doctrine\Common\Cache\ArrayCache,
    Doctrine\DBAL\Logging\EchoSQLLogger,
    Doctrine\ORM\Mapping\Driver\DatabaseDriver,
    Doctrine\ORM\Tools\DisconnectedClassMetadataFactory,
    Doctrine\ORM\Tools\EntityGenerator;

/**
 * CodeIgniter Doctrine Integration
 *
 * A custom library that initializes Doctrine ORM within CodeIgniter
 * 
 * @author Md. Ali Ahsan Rana
 * @link https://codesamplez.com/
 */
class Doctrine
{
    /**
     * @var EntityManager $em
     */
    public $em = null;

    /**
     * Constructor - Sets up Doctrine integration
     */
    public function __construct()
    {
        // Load database configuration from CodeIgniter
        require APPPATH.'config/database.php';

        // Set up class loading
        require_once APPPATH.'third_party/Doctrine/Common/ClassLoader.php';
        
        $doctrineClassLoader = new ClassLoader('Doctrine', APPPATH.'third_party');
        $doctrineClassLoader->register();
        
        $entitiesClassLoader = new ClassLoader('models', rtrim(APPPATH, "/" ));
        $entitiesClassLoader->register();
        
        $proxiesClassLoader = new ClassLoader('proxies', APPPATH.'models');
        $proxiesClassLoader->register();

        // Set up caches
        $config = new Configuration;
        $cache = new ArrayCache;
        $config->setMetadataCacheImpl($cache);
        
        // Set up driver
        $driverImpl = $config->newDefaultAnnotationDriver(array(APPPATH.'models/Entities'));
        $config->setMetadataDriverImpl($driverImpl);
        
        $config->setQueryCacheImpl($cache);

        // Proxy configuration
        $config->setProxyDir(APPPATH.'models/proxies');
        $config->setProxyNamespace('Proxies');
        
        // Enable auto-generation of proxy classes for development
        $config->setAutoGenerateProxyClasses(TRUE);

        // Database connection from CI config
        $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);
        
        // Uncomment the line below once to generate entities, then comment it out again
        // $this->generate_entities();
    }
    
    /**
     * Generate entity classes automatically from database tables
     * 
     * @return void
     */
    public function generate_entities()
    {
        // Register MySQL types with Doctrine
        $platform = $this->em->getConnection()->getDatabasePlatform();
        $platform->registerDoctrineTypeMapping('enum', 'string');
        
        // Set up metadata driver from database schema
        $this->em->getConfiguration()
            ->setMetadataDriverImpl(
                new DatabaseDriver(
                    $this->em->getConnection()->getSchemaManager()
                )
            );
        
        // Create metadata factory
        $cmf = new DisconnectedClassMetadataFactory();
        $cmf->setEntityManager($this->em);
        $metadata = $cmf->getAllMetadata();
        
        // Configure entity generator
        $generator = new EntityGenerator();
        $generator->setUpdateEntityIfExists(true);
        $generator->setGenerateStubMethods(true);
        $generator->setGenerateAnnotations(true);
        
        // Generate entities
        $generator->generate($metadata, APPPATH."models/Entities");
    }
}Code language: HTML, XML (xml)

IMPORTANT ⚠️: This implementation requires PHP 5.3+ because it uses namespaces. Make sure your server supports this version or higher.

Step 3: Create Required Directories

You’ll need to create two directories inside your ‘application/models’ folder:

  1. application/models/Entities – This will store your entity classes
  2. application/models/proxies – For Doctrine’s proxy classes

Step 4: Load the Library

To use Doctrine in your application, you need to load it. The best approach is to autoload it by adding ‘doctrine’ to your autoload.php config file:

$autoload['libraries'] = array('database', 'doctrine');Code language: PHP (php)

Generating Entity Classes From Your Database

One of the most powerful features of Doctrine is the ability to generate entity classes directly from your existing database structure. The generate_entities() method in our Doctrine library handles this process.

Pro Tip 💡: Only call the generate_entities() method once when you first set up your application or when your database structure changes. Comment it out after running it once to avoid performance issues.

How Entity Generation Works

The entity generation process:

  1. Examines your database schema using Doctrine’s DatabaseDriver
  2. Creates metadata for each table structure
  3. Generates PHP entity classes with proper annotations
  4. Places these classes in your application/models/Entities directory

When you run your application with the generate_entities() method uncommented, Doctrine will create entity classes for all your database tables.

A Real-World Example: Contact Form

Let’s apply what we’ve learned to a practical example – handling a contact form submission with Doctrine.

Step 1: Create the Database Table

First, create a table in your MySQL database:

CREATE TABLE `pd_contact` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `name` varchar(50) NOT NULL,
  `email` varchar(50) NOT NULL,
  `subject` varchar(100) NOT NULL,
  `message` text NOT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;Code language: JavaScript (javascript)

Step 2: Generate the Entity Class

After running your application with the generate_entities() method active, Doctrine will create a PdContact.php entity class in your application/models/Entities directory. It should look something like this:

<?php
/**
 * PdContact
 *
 * @Table(name="pd_contact")
 * @Entity
 */
class PdContact
{
    /**
     * @var integer $id
     *
     * @Column(name="id", type="integer", nullable=false)
     * @Id
     * @GeneratedValue(strategy="IDENTITY")
     */
    private $id;

    /**
     * @var string $name
     *
     * @Column(name="name", type="string", length=50, nullable=false)
     */
    private $name;

    /**
     * @var string $email
     *
     * @Column(name="email", type="string", length=50, nullable=false)
     */
    private $email;

    /**
     * @var string $subject
     *
     * @Column(name="subject", type="string", length=100, nullable=false)
     */
    private $subject;

    /**
     * @var text $message
     *
     * @Column(name="message", type="text", nullable=false)
     */
    private $message;

    // Getters and setters are automatically generated
    // ...
}Code language: HTML, XML (xml)

Step 3: Create a Model for Business Logic

Now, let’s create a model that will handle the business logic for our contact form:

<?php
require_once(APPPATH."models/Entities/PdContact.php");

use \PdContact;

/**
 * Home model for contact form operations
 */
class Homemodel extends CI_Model
{
    /**
     * @var \Doctrine\ORM\EntityManager $em
     */
    var $em;

    public function __construct()
    {
        parent::__construct();
        $this->em = $this->doctrine->em;
    }

    /**
     * Add contact messages to database
     * 
     * @return bool
     */
    function add_message()
    {
        // Create a new entity instance
        $contact = new PdContact();
        
        // Set properties from form data
        $contact->setName($this->input->post("name"));
        $contact->setEmail($this->input->post("email"));
        $contact->setSubject($this->input->post("subject"));
        $contact->setMessage($this->input->post("message"));

        try {
            // Save to database
            $this->em->persist($contact);
            $this->em->flush();
            return true;
        } catch(Exception $err) {
            log_message('error', $err->getMessage());
            return false;
        }
    }
    
    /**
     * Get all contact messages
     * 
     * @return array
     */
    function get_all_messages()
    {
        return $this->em->getRepository('PdContact')->findAll();
    }
}Code language: HTML, XML (xml)

Step 4: Use in Your Controller

Finally, you can use this model in your controller:

<?php
class Contact extends CI_Controller
{
    public function __construct()
    {
        parent::__construct();
        $this->load->model('homemodel');
    }
    
    public function index()
    {
        $this->load->view('contact_form');
    }
    
    public function submit()
    {
        if ($this->homemodel->add_message()) {
            $this->session->set_flashdata('success', 'Message sent successfully!');
        } else {
            $this->session->set_flashdata('error', 'Failed to send message.');
        }
        
        redirect('contact');
    }
}Code language: HTML, XML (xml)

Tip 💡: Level up your web development skill with our codeigniter learning roadmap

Using Doctrine’s Command Line Tools

Doctrine provides powerful command line tools for various operations like schema validation, entity generation, and more. Let’s set them up for use with CodeIgniter.

Creating the CLI Configuration

Create a file named cli-config.php in your project’s root directory:

<?php
$system_path = 'system';
$application_folder = 'application';

define('BASEPATH', str_replace("\\", "/", $system_path));
define('APPPATH', $application_folder.'/');

// Include composer autoloader if using composer
include __DIR__."/vendor/autoload.php";

// Include our Doctrine library
include __DIR__."/application/libraries/doctrine.php";

// Initialize Doctrine
$doctrine = new Doctrine();
$em = $doctrine->em;

// Set up helper set for command line tools
$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)
));

return $helperSet;Code language: HTML, XML (xml)

Using the Command Line Tools

Now you can use Doctrine’s command line tools. Here are some examples:

# Validate your schema
./vendor/bin/doctrine orm:validate-schema

# Generate proxy classes
./vendor/bin/doctrine orm:generate-proxies

# Create schema from entities
./vendor/bin/doctrine orm:schema-tool:createCode language: PHP (php)

Advanced Usage Tips

1. Working with Repository Classes

For complex queries, you can create custom repository classes:

<?php
// In Entity class
/**
 * @Entity(repositoryClass="PdContactRepository")
 */
class PdContact
{
    // ...
}

// In Repository class
class PdContactRepository extends \Doctrine\ORM\EntityRepository
{
    public function findRecent($limit = 5)
    {
        return $this->createQueryBuilder('c')
                    ->orderBy('c.id', 'DESC')
                    ->setMaxResults($limit)
                    ->getQuery()
                    ->getResult();
    }
}Code language: HTML, XML (xml)

2. Handling Relationships

Doctrine makes it easy to work with relationships between entities:

<?php
/**
 * @Entity
 */
class User
{
    /**
     * @OneToMany(targetEntity="PdContact", mappedBy="user")
     */
    private $contacts;
    
    public function __construct()
    {
        $this->contacts = new \Doctrine\Common\Collections\ArrayCollection();
    }
    
    // ...
}

/**
 * @Entity
 */
class PdContact
{
    /**
     * @ManyToOne(targetEntity="User", inversedBy="contacts")
     */
    private $user;
    
    // ...
}Code language: HTML, XML (xml)

3. Using DQL for Complex Queries

Doctrine Query Language (DQL) is extremely powerful for complex data retrieval:

$query = $this->em->createQuery('
    SELECT c FROM PdContact c
    WHERE c.email LIKE :email
    ORDER BY c.id DESC
');
$query->setParameter('email', '%gmail.com');
$results = $query->getResult();Code language: PHP (php)

Troubleshooting Common Issues

Issue 1: “Class is not a valid entity or mapped super class”

This often happens when:

  • You haven’t generated entity classes correctly
  • The namespace is wrong
  • You’re trying to use an entity without requiring/importing it

Solution: Make sure your entity class has the @Entity annotation and is properly imported.

Issue 2: “Unknown database type enum requested”

Doctrine doesn’t natively support MySQL’s ENUM type.

Solution: Add type mapping in the generate_entities() method:

$platform = $this->em->getConnection()->getDatabasePlatform();
$platform->registerDoctrineTypeMapping('enum', 'string');Code language: PHP (php)

Issue 3: “Failed to open stream: No such file or directory”

This usually indicates incorrect file paths.

Solution: Double-check your directory structure and make sure all paths are correct.

Conclusion

Integrating Doctrine with CodeIgniter gives you the best of both worlds – a lightweight, fast framework paired with a powerful ORM system. This combination provides incredible flexibility for your PHP applications, particularly when dealing with complex database operations.

By following this guide, you now have a robust integration that allows you to:

  • Use object-oriented database access
  • Generate entity classes automatically
  • Leverage Doctrine’s powerful query capabilities
  • Maintain clean, maintainable code

Remember to use the generate_entities() method only when necessary, and take advantage of Doctrine’s command line tools for advanced operations.

Have you implemented Doctrine with CodeIgniter in your projects? What challenges did you face? Share your experience in the comments below!

Happy coding with Doctrine and CodeIgniter!

Rana Ahsan

Rana Ahsan is a seasoned software engineer and technology leader specialized in distributed systems and software architecture. With a Master’s in Software Engineering from Concordia University, his experience spans leading scalable architecture at Coursera and TopHat, contributing to open-source projects. This blog, CodeSamplez.com, showcases his passion for sharing practical insights on programming and distributed systems concepts and help educate others. Github | X | LinkedIn

View Comments

  • Hi, thanks for this great tutorial. But i have a problem. When i close generate_classes() function, taking an error: Uncaught exception 'Doctrine\ORM\Mapping\MappingException' with message 'Class Actions is not a valid entity or mapped super class.'

    But after open this:
    $this->em->getConfiguration()
    ->setMetadataDriverImpl(
    new DatabaseDriver(
    $this->em->getConnection()->getSchemaManager()
    )
    );

    there's no error but website is very slow. What can i do? I do everything as you say.

  • function generate_classes()
    {
    $this->em->getConfiguration()
    ->setMetadataDriverImpl(
    new \Doctrine\ORM\Mapping\Driver\DatabaseDriver(
    $this->em->getConnection()->getSchemaManager()
    )
    );

    $cmf = new \Doctrine\ORM\Tools\DisconnectedClassMetadataFactory();
    $cmf->setEntityManager($this->em);
    $metadata = $cmf->getAllMetadata();
    $generator = new \Doctrine\ORM\Tools\EntityGenerator();

    $generator->setUpdateEntityIfExists(true);
    $generator->setGenerateStubMethods(true);
    $generator->setGenerateAnnotations(true);
    $generator->generate($metadata, APPPATH."models/Entities");
    }

  • hi im totally a newbie with this doctrine, and i willing to learn. i cant figure out how to integrate doctrine and CI, need help. thanks in advance. when i will call the generate classes? DQL is like HQL but netbeans IDE generates entities easily. need help with this doctrine stuff. thanks

    • you will need to call the generate class function only once, when you run the application on browser first time. Please let me know specifically in which other area you need help. Thanks.

  • Fatal error: Uncaught exception 'Doctrine\DBAL\DBALException' with message 'Unknown database type enum requested, Doctrine\DBAL\Platforms\MySqlPlatform may not support it.' in D:\Projects\ci\application\third_party\doctrine-orm\Doctrine\DBAL\Platforms\AbstractPlatform.php:210

    • Hi, when did you get this error please? did you created the databases and tables? My tutorial assumes so. by doctrine, its also possible to generate tables structure in db from running the application, but I didn't cover that part in this tutorial.

    • Hi hehmet soylu,
      You have to add just two lines of code in generate_class function.

      $platform = $this->em->getConnection()->getDatabasePlatform();
      $platform->registerDoctrineTypeMapping('enum', 'string');

      Thanks
      Alok

  • Class "pdcontact" sub class of "" is not a valid entity or mapped super class.

    i am facing this error what is the actual problem

    • You have probably forgot to create the table in database I guess. If not, can you please share the details error screenshot please?

  • Doesn't work for me! I used git clone...but I'm receiving this error message:
    Message: require_once(application/third_party/Doctrine/Common/ClassLoader.php) [function.require-once]: failed to open stream: No such file or directory

    Filename: libraries/Doctrine.php

    • Seems like, your file path is incorrect. can you please verify whether the file 'ClassLoader.php' is located at this path "application/third_party/Doctrine/Common/ClassLoader.php"?

  • Hi,

    That's nice but can you provide the download link for the source code.
    It would help the newbee(beginners a lot).

    Thanks
    Raj

    • Hi Raj, I actually don't have the code right now that I used on this tutorial. However, as a reference, you can use my 'codeigniterplus' project, https://github.com/ranacseruet/codeigniterplus, it has ready integration of doctrine in codeigniter and there I implemented some basic doctrine functionality. Hope it will help you. Let me know if you need anything else. Keep in touch. Thanks.

  • hi., can anybody help me., how to authenticate username and password stored in db..??? i am newbie to code ignitor and doctrine. help me plz..

  • How to fetch data in view?
    (
    [users] => Array
    (
    [0] => Users Object
    (
    [id:Users:private] => 1
    [ipAddress:Users:private] => 127.0.0.1
    [username:Users:private] => administrator
    [password:Users:private] => $2a$07$SeBknntpZror9uyftVopmu61qg0ms8Qv1yV6FG.kQOSM.9QhmTo36
    [salt:Users:private] =>
    [email:Users:private] => admin@admin.com
    [activationCode:Users:private] =>
    [forgottenPasswordCode:Users:private] =>
    [forgottenPasswordTime:Users:private] =>
    [rememberCode:Users:private] =>
    [createdOn:Users:private] => 1268889823
    [lastLogin:Users:private] => 1406821784
    [active:Users:private] => 1
    [firstName:Users:private] => Admin
    [lastName:Users:private] => istrator
    [company:Users:private] => ADMIN
    [phone:Users:private] => 0
    )

    )

    )

    • You can simply use "$users[0]->getEmail()" (iterate for multiple entries) and similar syntax to show if on view. Hope this helps!

Recent Posts

Python File Handling: A Beginner’s Complete Guide

Learn python file handling from scratch! This comprehensive guide walks you through reading, writing, and managing files in Python with real-world examples, troubleshooting tips, and…

5 days ago

Service Worker Best Practices: Security & Debugging Guide

You've conquered the service worker lifecycle, mastered caching strategies, and explored advanced features. Now it's time to lock down your implementation with battle-tested service worker…

2 weeks ago

Advanced Service Worker Features: Push Beyond the Basics

Unlock the full potential of service workers with advanced features like push notifications, background sync, and performance optimization techniques that transform your web app into…

4 weeks ago

This website uses cookies.