1 May/10

Doctrine and Zend Framework

Doctrine is one of the most common ORM tools for PHP and Zend Framework is one of the best PHP application frameworks. So, bringing together Doctrine and the Zend Framework is like bringing together vanilla and chocolate ice cream (or whatever your favorite ice cream flavors are ;-)).

ORM is short for Object Relational Mapping and is basically a concept that helps you to bridge the gap between objects in object-oriented programming languages and records in relational database systems. This tutorial will show you how to use Doctrine in a Zend_Framework application.

1. Installing Doctrine

Before we start: if you are already familiar with Doctrine you can directly jump to chapter 6.

If you haven’t done yet, go to http://www.doctrine-project.org/download and download the latest stable version of Doctrine. For this tutorial I recommend downloading the sandbox version. Unzip and you are done…

2. Configuration of your Database Connection

The next thing you have to do is to tell Doctrine how to connect to your database. We will use MySQL in this tutorial. Open the config.php file in your Doctrine folder and change the following line

$manager->openConnection(DSN, 'doctrine');

to something like

$manager->openConnection('mysql://user:password@server/database', 'connectionname');

Make sure that you adopt the connection parameters (user, password, etc.) to your configuration.

3. Your YAML file

Doctrine uses YAML (short for YAML Ain’t Markup Language) as description language for your database schema. In this tutorial we are going to implement a simple user management system (let’s call it SUMS) for a web portal. Our SUMS will store the name, date of birth, email and a password for each user. Furthermore, each user is going to have a unique ID which will also be our primary key.

Browse to your \schema folder in your Doctrine folder and open the schema.yml file. Copy and paste the following text:

---
User:
  connection: 0
  tableName: SUMS_USER
  columns:
    user_id:
      type: integer(4)
      fixed: false
      unsigned: false
      primary: true
      autoincrement: true
    user_name: string(100)
    user_email: string(100)
    user_dob: timestamp
    user_password: string(20)

As you can see, YAML supports both, a short (for instance user_name) and a verbose syntax (user_id). For more details on the YAML syntax, check the doctrine manual.

Attention! YAML uses line and whitespace delimiters so there is a big difference between “user_name” and “      user_name”! Moreover, YAML does not accept tabs, so instead of using “[tab]user_name” you have to use “      user_name”.

Now you are ready to start doctrine. Open a command line, jump to your doctrine folder and start doctrine using the following command:

php doctrine.php build-all-reload

If everything worked, you should see a table called SUMS_USER in your MySQL database.

4. Implementing some Logic

Well, this was pretty easy, wasn’t it. Actually, this example was even too easy to justify the use of Doctrine. So let’s implement some logic. Assume, that we would like to keep track of all changes in this table. Additionaly, we are going to implement a concept called “soft delete” which basically means, that instead of physically deleting a user from the table, we are going to mark it as deleted.

Wow, this sounds like a lot of work. But don’t panic – we will implement even more logic (and you will see, that all this is done with only a few additional lines in our YAML file). We want to make sure that the user_email attribute stores only valid emails and that the given date of birth is in the past.

Please note that this is something you could also implement using Zend Framework Validators, e.g. Zend_Validate_EmailAddress. However, if you have a three+ tier architecture where multiple applications access your database it makes sense to implement Doctrine’s validators.

Open your schema.yml file again and copy and paste the following lines:

---
User:
  connection: 0
  tableName: SUMS_USER
  actAs:
    SoftDelete:
    Versionable:
      versionColumn: version
      generateFiles: true
      generatePath: /path/to/your/models
      className: %CLASS%Version
      auditLog: true
  columns:
    user_id:
      type: integer(4)
      fixed: false
      unsigned: false
      primary: true
      autoincrement: true
    user_name: string(100)
    user_email:
      type: string(100)
      email: true
    user_dob:
      type: timestamp
      past: true
    user_password: string(20)

As I promissed, we implemented all this logic with only seven additional lines of code. Great, isn’t it? Actually, doctrine allows you to define even more: more behaviours, more validation rules, support of inheritence, etc. Just have a look at the doctrine manual to find out what doctrine can do for you.
Again, open a command line and start doctrine:

php doctrine.php build-all-reload

5. Doctrine Results

When executing doctrine.php with the buil-all-reload option Doctrine does the following for you: it automatically generates the specified database, the resulting tables plus some PHP source code. Basically, Doctrine will generate multiple PHP files for each table specified in the YAML file: A base class, a table class and a class that you may use to implement your logic. In our running example Doctrine will generate the following files:

  • BaseUser.php
  • UserTable.php
  • User.php

Without going into detail: these three files are needed by the active record pattern which doctrine implements. As a Doctrine beginner, you will probably only have to touch the User.php file to implement some logic. After generating the models, this file will look like this:

<?php

/**
 * User
 *
 * This class has been auto-generated by the Doctrine ORM Framework
 *
 * @package    ##PACKAGE##
 * @subpackage ##SUBPACKAGE##
 * @author     ##NAME## <##EMAIL##>
 * @version    SVN: $Id: Builder.php 7490 2010-03-29 19:53:27Z jwage $
 */
class User extends BaseUser
{
 public function init() {
 }
}

6. Putting it All Together

Before using Doctrine in your PHP / Zend Framework application you have to copy Doctrine’s \lib directory into your application. I recommend storing it in your \library folder.

Now, copy all the files from your \doctrine\models\ folder into the folder where your application stores all the models (which will probably be \application\models). Let me emphasize one important thing: copying the files from your doctrine to your models folder is definitely not the best way to interdigitate Doctrine and the Zend Framework. However, it will do the job for this tutorial. Among others, one smarter way to deal with this issue would be to adopt Doctrine’s MODELS_PATH… For more details, please have a look at the Doctrine manual.

The last thing on your To-Do list is make sure that PHP is able to find all the corresponding classes and to initialize doctrine in your application. Adopt your index.php and bootstrap.php like this:

index.php

// Define include paths
$paths = array(
    APPLICATION_PATH . '/application/models',
    APPLICATION_PATH . '/library/Doctrine',
    APPLICATION_PATH . '/library',
    APPLICATION_PATH . '/library/Zend',
    '.'
);

// Init your include path
ini_set('include_path', implode(PATH_SEPARATOR, $paths));

bootstrap.php

    require_once 'Doctrine.php';
    $loader = Zend_Loader_Autoloader::getInstance();
    $loader->pushAutoloader(array('Doctrine', 'autoload'));
    $manager = Doctrine_Manager::getInstance();
    $manager->setAttribute(
        Doctrine::ATTR_MODEL_LOADING,
        Doctrine::MODEL_LOADING_CONSERVATIVE);
    Doctrine::loadModels(APPLICATION_PATH . '/application/models');
    // make sure that you adopt this line:
    $manager->openConnection("mysql://user:password@server/doctrinetest");

7. A Small Example

Actually, you are already done integrating Doctrine and Zend Framework. Here is a small example how to use this user class in a controller.

<?php
/**
 * A very simple action for a simple controller
 *
 * @package    DoctrineTest
 */
class IndexController extends Zend_Controller_Action
{
  function indexAction()   {
     // generate new user
     $u1 = new User();
     $u1->user_name = "Christian Koncilia";
     $u1->user_dob = date('Y-m-d H:i:s',mktime(0,0,0,12,25,1969));
     $u1->user_password = "mypassword";
     $u1->user_email = "admin@php-punk.com";
     // save this user to database
     $u1->save();

     // fetch a user
     $u2 = new User();
     $u2 = Doctrine::getTable("user")->find(1);
     // change this users name and save to database
     $u2->user_name = "Max Mayer";
     $u2->save();

     // now, have a look at your SUMS_USER and USER_VERSION table...
 }
}

Posted in Doctrine and Tutorial and Zend Framework by Christian on May 1st, 2010 at 6:26 PM.

4 comments - 3 pingbacks / trackbacks

4 Replies

  1. Hey ,
    I was going through zendcasts doctrine and have made some changes, so the doctrine is well enough to create the models on modules folder also :) . You can check it here

    http://harikt.com/content/integrating-doctrine-zendframework-modules

    Let me know what you think :)

  2. Hi Hari,

    at a first glance your post looks great (I am away on leave at the seaside and didn’t had the time for a closer look ;-)). Could you post an example on your blog how to use the code?

    Bye
    Christian

  3. Lovely Mar 15th 2011

    Hello Guys,

    I am trying setup zend 1.1 with doctrine. but U getting some erros. can any body send me dummy setup or project.

    Thanks a lot

  4. Hi,

    Zend Framework 1.1 is outdated. I would strongly recommend to upgrade to 1.11.

    If you follow the turorial given above step by step Doctrine should work fine. If it doesn’t, you should let us know what “some errors” exactly means ;-)

    Bye
    Christian


Leave a Reply



Catchable fatal error: Object of class stdClass could not be converted to string in /kunden/223429_82008/phppunk/wp-content/plugins/wp-slimstat/wp-slimstat.php on line 1007