TAAT Technologie Cyfrowe

Zend Framework Tutorial

Plugging in Doctrine ORM to Zend Framework

First of all, you need to Download Doctrine ORM and put it in /library/ directory. We need Doctrine.php and Doctrine there.

Second, we need a database to work with. I use PostgreSQL engine (pgsql). Both on development, and live site, but different databases. So we add two database configurations in the /application/config/config.ini:

[global]

; global project settings
project.name = My project
timezone = Europe/Warsaw

; development settings
[development : global]
debug = on
project.name = "My project (development)"

db.adapter  = pgsql
db.host     = localhost
db.dbname   = databasedev
db.username = youruser
db.password = yourpassword

; live settings
[live : global]
debug = off
project.name = "My project (live)"

db.adapter  = pgsql
db.host     = localhost
db.dbname   = databaselive
db.username = otheruser
db.password = otherpassword

No matter what database do you use (PostgreSQL, MySQL, Oracle or other). Just change db.adapter to suit your needs.

Then need some folders for Doctrine files. So we have to create following structure on disk, apply required rights:

/application/doctrine/models/
/application/doctrine/models/generated/
/application/doctrine/data/
/application/doctrine/data/fixtures/
/application/doctrine/data/sql/
/application/doctrine/migrations/
/application/doctrine/schema/

Then tell Doctrine where these directories are. This information is also stored in the config.ini:

[global]

; global project settings
project.name = My project
timezone = Europe/Warsaw

; doctrine settings
doctrine.data_fixtures_path = application/doctrine/data/fixtures
doctrine.models_path = application/doctrine/models
doctrine.migrations_path = application/doctrine/migrations
doctrine.sql_path = application/doctrine/data/sql
doctrine.yaml_schema_path = application/doctrine/schema

; development settings
[development : global]
debug = on
project.name = "My project (development)"

db.adapter  = pgsql
db.host     = localhost
db.dbname   = databasedev
db.username = youruser
db.password = yourpassword

; live settings
[live : global]
debug = off
project.name = "My project (live)"

db.adapter  = pgsql
db.host     = localhost
db.dbname   = databaselive
db.username = otheruser
db.password = otherpassword

You should already know that best way to create plugin for Zend Framework is to write Action Controller Plugin. But since we may need Doctrine also outside dispatch loop (for example in command line CLI), it is better to create separate static method for it.

Basic Doctrine plugin (/library/My/Controller/Plugin/Doctrine.php) looks like this:

<?php
require_once 'Zend/Controller/Plugin/Abstract.php';
/**
* Zend_Controller_Plugin preparing Doctrine ORM
*/
class My_Controller_Plugin_Doctrine extends Zend_Controller_Plugin_Abstract
{
public function dispatchLoopStartup (Zend_Controller_Request_Abstract $request)
{
        self::setupDoctrine();
        self::setupSession();
}

/**
 * setupDoctrine
 * @access public static
 * @param
 * @return null
 */
public static function setupDoctrine() {
        $config = Zend_Registry::get('config');
        $connection = Doctrine_Manager::connection(sprintf(
                                                                                 '%s://%s:%s@%s/%s',
                                                                                 strtolower($config->db->adapter),
                                                                                 $config->db->username,
                                                                                 $config->db->password,
                                                                                 $config->db->host,
                                                                                 $config->db->dbname
                                                                                 )
                                                                 );

        // set connection manager attributies here
        // $manager = Doctrine_Manager::getInstance();
        // $manager->setAttribute(Doctrine::ATTR_VALIDATE, Doctrine::VALIDATE_ALL);
        // $manager->setAttribute(Doctrine::ATTR_EXPORT, Doctrine::EXPORT_ALL);
        // $manager->setAttribute(Doctrine::ATTR_AUTOLOAD_TABLE_CLASSES, true);
        // $manager->setAttribute(Doctrine::ATTR_USE_DQL_CALLBACKS, true);
        // $manager->setAttribute(Doctrine::ATTR_AUTO_ACCESSOR_OVERRIDE, true);
        // $manager->setAttribute(Doctrine::ATTR_QUOTE_IDENTIFIER, true);
        // $manager->setAttribute(Doctrine::ATTR_AUTOLOAD_TABLE_CLASSES, false);

        $configDoctrine = $config->doctrine->toArray();
        foreach ($configDoctrine as $key => $value) {
                $configDoctrine[$key] = $value[0] === '/' ? Bootstrap::$root . $value : Bootstrap::$root . '/' . $value;
        }
        Zend_Registry::set('config_doctrine', $configDoctrine);
}

        /**
         * Start session and handle it using Doctrine
         * @access public static
         * @return null
         */
        public static function setupSession() {
        Zend_Session::setSaveHandler(new My_Session_Doctrine());
        Zend_Session::start();
        }

}

Since we want to handle session data, we add to this plugin one more method: setupSession(). It uses custom session class, which looks like this (/library/My/Session/Doctrine.php):

<?php

class My_Session_Doctrine implements Zend_Session_SaveHandler_Interface
{
        private $_sessionName;
        private $_session;
        private $_lifetime;

        public function __construct($lifetime = null)
        {
                if(is_null($lifetime))
                {
                        $this->_lifetime = (int) ini_get('session.gc_maxlifetime');
                }
                else
                {
                        $this->_lifetime = (int) $lifetime;
                }
        }

        public function setLifetime($lifetime)
        {
                $this->_lifetime = $lifetime;
        }

        public function getLifetime()
        {
                return $this->_lifetime;
        }

        public function read($id)
        {
                $this->_session = Doctrine::getTable('Session')->find($id);

                if(empty($this->_session))
                {
                        $this->_session = new Session();
                        $this->_session->id = $id;
                        $this->_session->lifetime = $this->_lifetime;

                        return '';
                }
                return $this->_session->data;
        }

        public function write($id, $data)
        {
                $this->_session->data = $data;
                $this->_session->modified = time();
                $this->_session->save();

                return true;
        }

        public function destroy($id)
        {
                if($this->_session->id == $id)
                {
                        $this->_session->delete();
                        return true;
                }
                return false;
        }

        public function gc($maxlifetime)
        {
                Doctrine_Query::create()->delete('Session s')->where('s.modified < (? - s.lifetime)', time())->execute();
        }

        public function open($save_path, $name)
        {
                $this->_sessionName = $name;
                return true;
        }

        public function close()
        {
                return true;
        }
}

Finally, we need to hook Doctrine in our Bootstrap.php, creating setupDoctrine() method and calling it in setup() method:

/**
 * Global setup
 * @access public static
 * @return null
 */
public static function setup()
{
        self::setupConfig();
        self::setupEnvironment();
        self::setupFrontController();
        self::setupView();
        self::setupDatabase();
}

/**
 * Setup Database
 * @access public static
 * @return null
 */
public static function setupDatabase() {
        My_Controller_Plugin_Doctrine::setupDoctrine();
        self::setupFirePHP(Zend_Registry::get('config')->debug);
        My_Controller_Plugin_Doctrine::setupSession();

        // or if you don't need database in Bootstrap
        // self::$frontController->registerPlugin(new My_Controller_Plugin_Doctrine());
}
 

We also need to add doctrine model directories to the include_path in setupEnvironment():

/**
 * Setup environment
 * @access public static
 * @param
 * @return null
 */
public static function setupEnvironment ()
{
        self::$root = dirname(dirname(__FILE__));
        set_include_path(
                                         get_include_path() .
                                         PATH_SEPARATOR . self::$root . '/library/'
                                        );
        // doctrine paths
        $doctrine_models = Zend_Registry::get('config')->doctrine->models_path;
        set_include_path(
                                         get_include_path()
                                         . PATH_SEPARATOR .  self::$root . '/' . $doctrine_models
                                         . PATH_SEPARATOR .  self::$root . '/' . $doctrine_models . '/generated'
                                         );
}

Session data will be stored in database table of following structure /application/doctrine/schema/session.yml

---
Session:
    columns:
        id:
            type: string(32)
            primary: true
        modified: integer(4)
        lifetime: integer(4)
        data: clob

To use doctrine-cli from the command line, we will need three following scripts:

/scripts/doctrine-cli.sh:

#!/usr/bin/env php
<?php
require '../application/Bootstrap.php';
putenv('DEVELOPMENT=true');
Zend_Loader::registerAutoload();
Bootstrap::setupConfig();
Bootstrap::setupEnvironment();
My_Controller_Plugin_Doctrine::setupDoctrine();
$cli = new Doctrine_Cli(Zend_Registry::get('config_doctrine'));
$cli->run($_SERVER['argv']);

/scripts/doctrine-cli.bat:

@echo off
IF EXIST "C:\program files\apache\php5\php.exe" GOTO :START
:ERROR
ECHO Error: wrong path to php.exe
ECHO Edit doctrine-cli.bat to set up correct path
GOTO :END
:START
"C:\program files\apache\php5\php.exe" doctrine-cli.sh %1
:END

/scripts/create-from-yml.bat:

@echo off
call doctrine-cli.bat generate-models-yaml
call doctrine-cli.bat generate-sql
call doctrine-cli.bat create-tables

Assuming you already created database and provided valid settings in config.ini, running create-from-yml.bat should create classes and tables for session handling by Doctrine.

And that’s all for now. See Doctrine Documentation for more details.

Next step