Setting up CodeIgniter 2 with Doctrine 2 the right way
So you've discovered the awesomeness of CodeIgniter and you need an ORM. You've read about people around StackOverflow and the CodeIgniter forums recommending Doctrine as an object mapper. You've tried and tried but Doctrine and CodeIgniter are just not playing nicely together. Well http://www.phpandstuff.com/ has a stupendous tutorial... for CodeIgniter < 2.0 and Doctrine < 2.0. We need something more recent, eh?
Well, I walked the same rocky road and decided to make my own tutorial. Yah, yah, there's already a bunch out there. But they either suck or don't apply to CodeIgniter 2 or Doctrine 2. Mine doesn't suck. I hope.
So for future reference, here are the versions I am working with during this tutorial, but similarly versions should work just fine:
Also, I am running Apache 2.2.20, PHP 5.3.6 (with APC), MySQL 5.1.58, all on Ubuntu Server 11.10.
I am assuming you already have a MySQL database (with valid a user) set up.
Versions
The following versions have been tested to make sure they fully work with this tutorial:
CodeIgniter 2.1.0 and Doctrine 2.2.1
CodeIgniter 2.0.2 and Doctrine 2.0.5
If you want to work with a different version of CodeIgniter or Doctrine, this tutorial should still be totally fine as long as the version of CodeIgniterand Doctrine are version 2.x.x! You may have to change the paths of downloads a bit (i.e. changing a CodeIgniter_2.1.0.zip in a URL to CodeIgniter_2.0.2.zip, but everything else will be exactly the same. If you can't handle that, I'd recommend firing up a WordPress instance and going from there...
Tutorial Context
Also, here are some names I will be using throughout the tutorial. Replace them with whatever paths or names that are relevant to your project.
Project Name: Emma Watson Shrine
Project Directory:/var/www/emma_watson_shrine/
I am going to try to make this as easy on you as possible, so I will give all of the steps necessary to get this stack up and running. In other words, you shouldn't even have to read the Doctrine and CodeIgniter documentation, although it is HIGHLY recommended.
"And away we go..."
Download and Configure CodeIgniter
Download the CodeIgniter archive, extract it, and move it to the location that you'd like your project to reside.
### go to the web directorycd /var/www
### download CodeIgniter
wget http://www.codeigniter.com/download_files/reactor/CodeIgniter_2.1.0.zip
### unzip the archive
unzip CodeIgniter_2.1.0.zip
### remove the archiverm CodeIgniter_2.1.0.zip
### rename the extracted archive to the desired project directorymv CodeIgniter_2.1.0 emma_watson_shrine
Make sure your project's directory has proper permissions. This kind of depends on your server's setup, but I set my project's directory permissions to 755. If you're not sure about this, skip to the next step. It's most likely that the permissions are okay if you used an FTP program to upload the CodeIgniter files.
chmod-R 755 emma_watson_shrine
CodeIgniter still needs some configuration, but it's running! When you view your project directory in your browser, it should look something like this: screenshot.
http://www.example.com/emma_watson_shrine/
Now let's take some time to configure CodeIgniter. Don't worry, it's really quick!
Open CodeIgniter's config file in the text editor of your choice.
vim /var/www/emma_watson_shrine/application/config/config.php
And that's really all there is to it! CodeIgniter, for the win!
Download Doctrine
Download the Doctrine archive, extract it, and move it to your CodeIgniter application's library directory. Keep in mind that the archive you download has two sub-directories: bin and Doctrine. The Doctrine directory is what you actually need.
### go to the libraries directorycd /var/www/emma_watson_shrine/application/libraries
### download Doctrine
wget http://www.doctrine-project.org/downloads/DoctrineORM-2.2.1-full.tar.gz
### extract the archivetar xfz DoctrineORM-2.2.1-full.tar.gz
### pull out the directory we needmv DoctrineORM-2.2.1/Doctrine Doctrine
### remove the other stuff we don't needrm-rf DoctrineORM-2.2.1 DoctrineORM-2.2.1-full.tar.gz
Add Doctrine as a CodeIgniter library
This is when things start getting a bit more confusing. So read carefully!
Go to your CodeIgniter applications's library directory. We need to make a PHP class that will help our CodeIgniter controllers talk use Doctrine's all-powerful Entity Manager. Create a new file at the following path and open it up in a text editor:
vim /var/www/emma_watson_shrine/application/libraries/Doctrine.php
Paste this code in. Read the in-code comments for explanation, if you want to learn a thing or two. Here's a link to Doctrine.php if you just want to download it to the right location.
<?phpclassDoctrine{// the Doctrine entity managerpublic$em=null;publicfunction__construct(){// include our CodeIgniter application's database configurationrequire_onceAPPPATH.'config/database.php';// include Doctrine's fancy ClassLoader classrequire_onceAPPPATH.'libraries/Doctrine/Common/ClassLoader.php';// load the Doctrine classes$doctrineClassLoader=new\Doctrine\Common\ClassLoader('Doctrine',APPPATH.'libraries');$doctrineClassLoader->register();// load Symfony2 helpers// Don't be alarmed, this is necessary for YAML mapping files$symfonyClassLoader=new\Doctrine\Common\ClassLoader('Symfony',APPPATH.'libraries/Doctrine');$symfonyClassLoader->register();// load the entities$entityClassLoader=new\Doctrine\Common\ClassLoader('Entities',APPPATH.'models');$entityClassLoader->register();// load the proxy entities$proxyClassLoader=new\Doctrine\Common\ClassLoader('Proxies',APPPATH.'models');$proxyClassLoader->register();// set up the configuration $config=new\Doctrine\ORM\Configuration;if(ENVIRONMENT=='development')// set up simple array caching for development mode$cache=new\Doctrine\Common\Cache\ArrayCache;else// set up caching with APC for production mode$cache=new\Doctrine\Common\Cache\ApcCache;$config->setMetadataCacheImpl($cache);$config->setQueryCacheImpl($cache);// set up proxy configuration$config->setProxyDir(APPPATH.'models/Proxies');$config->setProxyNamespace('Proxies');// auto-generate proxy classes if we are in development mode$config->setAutoGenerateProxyClasses(ENVIRONMENT=='development');// set up annotation driver$yamlDriver=new\Doctrine\ORM\Mapping\Driver\YamlDriver(APPPATH.'models/Mappings');$config->setMetadataDriverImpl($yamlDriver);// 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 the EntityManager$em=\Doctrine\ORM\EntityManager::create($connectionOptions,$config);// store it as a member, for use in our CodeIgniter controllers.$this->em=$em;}}
Open CodeIgniter's autoload config file.
vim /var/www/emma_watson_shrine/application/config/autoload.php
Add doctrine to the autoloaded libraries.
<?php$autoload['libraries']=array('doctrine');
You should be able to view your project directory in your browser at this point without any errors coming up. If you have, double back and make sure you've followed all the steps. Furthermore, the page should look exactly as it did before you added Doctrine as a library.
http://www.example.com/emma_watson_shrine/
Add Models to Doctrine
From here on, what you need to do really depends on what you would like your website to do.
Since I am making a shrine dedicated to Emma Watson, I think I'll need two models (represented by two tables): a User object and an Article object. The User object will hold information about an individual user. The Article object will have an author (linking to a User object) and other information for displaying an article.
Something we need to get straight first is that the models generated by Doctrine are going to be different than CodeIgniter models (i.e. they will not extend the CI_Model class).
Create a Mappings directory in your application’s models directory.
cd /var/www/emma_watson_shrine/application/models
mkdir Mappings
cd Mappings
Create some YAML mapping files and put them in the Mappings directory. How to do this is outside the scope of this tutorial, but I will provide mine as examples. Here is a link to the Doctrine documentation on how to format these file. Make sure the mapping files have the ".dcm.yml" file extension and the "Entities." prefix. The extension tells Doctrine that this file is a YAML mapping file, and the prefix tells Doctrine to put the models in the Entity namespace.
When declaring the name of your object, make sure to prefix the name with Entities\ to put it in the Entities namespace.
Here is the YAML file for my User object. The file name should be Entities.User.dcm.yml.
Go back to the models directory and create two more directories: "Entities" and "Proxies". Proxies is the directory that will hold the proxy classes that Doctrine uses. If you're really smart, you should be able to figure out what the Entities directory is for. Since this is the Internet, I think it's safe to assume that I should explain it to you. It's the directory that holds the Entities!
cd /var/www/emma_watson_shrine/application/models
mkdir Entities Proxies
Configure Doctrine Command Line Tool
To use Doctrine, you need set up the command line tool, which helps you with a lot of tasks, like automatically creating the schema in your database, generating proxy classes, etc.
Go to your application's application directory and create a file called doctrine-cli.php. This is a PHP file that you will need to run through the command line PHP program. Alternatively, download this file to your application directory and skip the next step. Make sure you change the APPPATH definition in the code to fit your needs!
cd /var/www/emma_watson_shrine/application
vim doctrine-cli.php
Put the following contents in your doctrine-cli.php file. As I said above, make sure you change the APPPATH in the code definition to fit your needs!
<?php// trailing slash is important!define('APPPATH',dirname(__FILE__).DIRECTORY_SEPARATOR);define('BASEPATH',APPPATH);define('ENVIRONMENT','production');requireAPPPATH.'libraries/Doctrine.php';$doctrine=newDoctrine();$helperSet=new\Symfony\Component\Console\Helper\HelperSet(array('db'=>new\Doctrine\DBAL\Tools\Console\Helper\ConnectionHelper($doctrine->em->getConnection()),'em'=>new\Doctrine\ORM\Tools\Console\Helper\EntityManagerHelper($doctrine->em)));$cli=new\Symfony\Component\Console\Application('Doctrine Command Line Interface (CodeIgniter integration by Joel Verhagen)',Doctrine\ORM\Version::VERSION);$cli->setCatchExceptions(true);$cli->setHelperSet($helperSet);$cli->addCommands(array(// DBAL Commandsnew\Doctrine\DBAL\Tools\Console\Command\RunSqlCommand(),new\Doctrine\DBAL\Tools\Console\Command\ImportCommand(),// ORM Commandsnew\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();
Now run this script through the PHP command-line and should see a list of commands available to you.
php doctrine-cli.php
The output should look something like this:
Doctrine Command Line Interface (CodeIgniter integration by Joel Verhagen) version 2.0.5
Usage:
[options] command [arguments]
Options:
--help -h Display this help message.
--quiet -q Do not output any message.
--verbose -v Increase verbosity of messages.
--version -V Display this program version.
--ansi -a Force ANSI output.
--no-interaction -n Do not ask any interactive question.
Available commands:
help Displays help for a command (?)
list Lists commands
dbal
:import Import SQL file(s) directly to Database.
:run-sql Executes arbitrary SQL directly from the command line.
orm
:convert-d1-schema Converts Doctrine 1.X schema into a Doctrine 2.X schema.
:convert-mapping Convert mapping information between supported formats.
:ensure-production-settings Verify that Doctrine is properly configured for a production environment.
:generate-entities Generate entity classes and method stubs from your mapping information.
:generate-proxies Generates proxy classes for entity classes.
:generate-repositories Generate repository classes from your mapping information.
:run-dql Executes arbitrary DQL directly from the command line.
:validate-schema Validate that the mapping files.
orm:clear-cache
:metadata Clear all metadata cache of the various cache drivers.
:query Clear all query cache of the various cache drivers.
:result Clear result cache of the various cache drivers.
orm:schema-tool
:create Processes the schema and either create it directly on EntityManager Storage Connection or generate the SQL output.
:drop Drop the complete database schema of EntityManager Storage Connection or generate the corresponding SQL output.
:update Processes the schema and either update the database schema of EntityManager Storage Connection or generate the SQL output.
Setup Entities, Proxies, and Database Schema
We're getting close here. Really the last thing you need to do before you can jump into developing your Facebook killer.
For all of the steps in this section, you'll need to simply call the Doctrine command line tool that you got working in the previous section. The order of these steps does matter!
cd /var/www/emma_watson_shrine/application
Let's go ahead and generate the entity classes. Note that we are creating them in the models directory. Doctrine detects that they are in the Entities namespace and automatically puts them in the Entities subdirectory. Keep in mind that these entity classes come with getters/setters for all of the fields you defined in your YAML file but can be modified to add custom methods and such.
php doctrine-cli.php orm:generate-entities models
Now the proxy classes.
php doctrine-cli.php orm:generate-proxies
If you've tried this before and your database is not empty, you'll need to do this step. Otherwise, skip it.
php doctrine-cli.php orm:schema-tool:drop --force
And finally, let's create the database tables for Doctrine to store our objects in.
php doctrine-cli.php orm:schema-tool:create
Using Doctrine in a CodeIgniter Controller
Believe it or not, you've just made it through all the hard stuff. With all that stupid configuration stuff out of the way, you can get to coding. Yay! Since we added Doctrine as an autoloaded library, we don't even need to fool with loading Doctrine every time you create a new controller. Also, the library file (libraries/Doctrine.php) has a auto-load call that makes it so all of the entities you put in the models/Entities directory are automatically available to your controllers!
If you know what you're doing, go ahead and leave this page. Get coding!
Otherwise, I'm going to give you a few code samples so you can start using Doctrine.
How do I create a new entity and save it into the database?
Easy.
<?phppublicfunctioncreateObjects(){// create a new user object$user=newEntities\User;$user->setFirstName('Joel');$user->setLastName('Verhagen');$user->setPassword(md5('Emma Watson'));$user->setEmail('[email protected]');$user->setWebsite('https://www.joelverhagen.com');$user->setCreated(newDateTime());// standard way in CodeIgniter to access a library in a controller: $this->library_name->member->memberFunction()// save the object to database, so that it can obtain its ID$this->doctrine->em->persist($user);// create a new article object$article=newEntities\Article;$article->setTitle('Emma Watson is extremely talented, no?');$article->setContent('By talented, I mean good at lookin\' good.');$article->setCreated(newDateTime());// the user object you pass must be persisted first!$article->setUser($user);// save the article object to the database$this->doctrine->em->persist($article);$this->doctrine->em->flush();echo"<b>Success!</b>";}
How do I get an entity from the database?
Child's play.
<?phppublicfunctiongetObjects(){// get an object by ID$user=$this->doctrine->em->find('Entities\User',1);echo$user->getFirstName().' '.$user->getLastName().' is a real chill guy.<br />';// get a related object$article=$this->doctrine->em->find('Entities\Article',1);$user=$article->getUser();echo$user->getFirstName().' '.$user->getLastName().' thinks CodeIgniter + Doctrine is real slick. <br />';// what happens when we try to get an object that doesn't exist?$article=$this->doctrine->em->find('Entities\Article',9001);if(is_null($article)){// the "find" call returns NULL!echo'Dude, that article number 9001 doesn\'t even exist yet.<br />';}// get an object by another field$user=$this->doctrine->em->getRepository('Entities\User')->findOneBy(array('email'=>'[email protected]'));echo$user->getFirstName().' '.$user->getLastName().' is glad he isn\'t dealing with Symfony.<br />';}
Can I add custom methods to my entities?
Yes!
<?phpnamespaceEntities;/**
* Entities\User
*/classUser{...// my custom function :)publicfunctiongetFullName(){return$this->firstName.' '.$this->lastName;}...}