The perfect marriage?
That Zend Framework is one of the best positioned framework for developing PHP web applications, it’s a fact. It can be used as a component stack, or as a full MVC stack taking advantage of the rest of the components.
The tendency, it’s clear. ZF follows the line defined by Ruby on Rails and the rest of the frameworks, in the way that all (or almost all) the conventions already defined by others, prevail on this. And this implies a better and easier learning step by all the people than want to get started on. But it’s not gold, all that glitters (typical Spanish proverb). At least, from my point of view, I quite like the layer that intereacts with all the domain model (the Zend_Db components family), beacause I think that It hasn’t still became to an O/RM properly. Despite of implement a design pattern based on Table Data Gateway and on Row Data Gateway.
For the domain model layer interaction, I try to use the Doctrine 2 project (I haven’t use it in all of my projects). Doctrine 2 is an O/RM, also strongly based on Data Mapper. Features like database introspection for full schema generation, code annotation based models (@Anotations == POPOs — Plain Old PHP Objects), or it’s design based on Data Mapper (and on the mythic Hibernate java project) makes it a good option. But, going to the point, now it comes the great question …
What integration possibilities there are between the two projects?
Actually, all. Both are very powerful projects, totally integrable between them. And on this post I’ll describe minutely all the process. This post pretend to be the first of a series of blog posts related to the development under Zend Framework. So, there will be more.
Understanding Zend Framework
To begin, Zend Framework has a default directory layout. This is a directory where all the application resources are saved, a directory where to store all the external libraries, another as a public directory and another as a store for all the test for our application.
Following the Zend Framework manual, it’s really easy to create a new project. So I suggest to follow the manual if someone doesn’t now how to get started on the ZF development.
An interesting feature about Zend Framework is that allows to the developer to interact on the Bootstrap phase, to inject all the needed components. And this is allowed in way that I consider so so elegant: by the Zend_Application_Resource_ResourceAbstract component. This class will allow us to define an interface between ZF and Doctrine 2, in a way that It will allow us to integrate all the domain model generated by Doctrine 2 on the Zend context.
The commitment
/**
* File located in library/Doctrine/Zend/Doctrine.php
*/
require_once 'Zend/Application/Resource/ResourceAbstract.php';
require_once 'Doctrine/Common/ClassLoader.php';
class Doctrine_Zend_Doctrine
extends Zend_Application_Resource_ResourceAbstract
{
public function init()
{
$options = $this -> getOptions();
// Doctrine (use include_path)
$classLoader = new \Doctrine\Common\ClassLoader('Doctrine');
$classLoader -> register();
// Entities
$classLoader = new \Doctrine\Common\ClassLoader(
'Application\Models',
dirname(APPLICATION_PATH) . DIRECTORY_SEPARATOR . 'library'
);
$classLoader -> register();
// Proxies
$classLoader = new \Doctrine\Common\ClassLoader(
'Application\Models\Proxies',
dirname(APPLICATION_PATH) . DIRECTORY_SEPARATOR . 'library'
);
$classLoader -> register();
// Now configure doctrine
if ('development' == APPLICATION_ENV) {
$cache = new \Doctrine\Common\Cache\ArrayCache();
} else {
$cache = new \Doctrine\Common\Cache\ApcCache();
}
$config = new Configuration();
$config -> setMetadataCacheImpl($cache);
$driverImpl = $config -> newDefaultAnnotationDriver($options['entitiesPath']);
$config -> setMetadataDriverImpl($driverImpl);
$config -> setQueryCacheImpl($cache);
$config -> setProxyDir($options['proxiesPath']);
$config -> setProxyNamespace('Application\Models\Proxies');
$config -> setAutoGenerateProxyClasses(('development' == APPLICATION_ENV));
$em = EntityManager::create(
$this -> _buildConnectionOptions($options),
$config
);
// Once we have the EntityManager ready, add it to the registry
Zend_Registry::set('em', $em);
// end
return $em;
}
/**
* A method to build the connection options, for a Doctrine
* EntityManager/Connection. Sure, we can find a more elegant solution to build
* the connection options. A builder class could be applied. Sure you can with
* some refactor!
* TODO: refactor to build some other, more elegant, solution to build the conn
* ection object.
* @param Array $options The options array defined on the application.ini file
* @return Array
*/
protected function _buildConnectionOptions($options)
{
$connectionSpec = array(
'pdo_sqlite' => array('user', 'password', 'path', 'memory'),
'pdo_mysql' => array(
'user', 'password', 'host', 'port', 'dbname', 'unix_socket'
),
'pdo_pgsql' => array('user', 'password', 'host', 'port', 'dbname'),
'pdo_oci' => array(
'user', 'password', 'host', 'port', 'dbname', 'charset'
)
);
$connection = array(
'driver' => $options['driver']
);
foreach ($connectionSpec[$options['driver']] as $driverOption) {
if (isset($options[$driverOption]) && !is_null($driverOption)) {
$connection[$driverOption] = $options[$driverOption];
}
}
if (isset($options['driverOptions'])
&& !is_null($options['driverOptions'])) {
$connection['driverOptions'] = $options['driverOptions'];
}
return $connection;
}
}
With this on our include_path, It cannot be anymoure doubts … We are going to ask for the hand
Asking for the hand
With this class on the ZF execution context, Doctrine 2 can be configured by the application.ini file.
; ... [production] ; Doctrine 2 Config pluginPaths.Doctrine_Zend_ = "Doctrine/Zend/" resources.doctrine.entitiesPath = APPLICATION_PATH "/../library/models" resources.doctrine.proxiesPath = APPLICATION_PATH "/../library/models/proxies" resources.doctrine.driver = pdo_sqlite resources.doctrine.path = APPLICATION_PATH "/../data/db/database-dev.db"
The next step, would be to generate all the entities and the proxies needed. This can be done by the Doctrine 2 console tool (next post subject: integration between the Doctrine 2 console tool and the ZF tool). And finally …
The Wedding
Words are no needed, the code speaks by itself.
<?php
class SectionController extends Zend_Controller_Action
{
public function init()
{
/* Initialize action controller here */
}
public function indexAction()
{
$sections = Zend_Registry::get('em')
-> createQuery('select s from \Application\Models\Section s')
-> getResult();
$this -> view -> sections = $sections;
}
}
And only remains to be seen flying through the air of the rice grains and the typical wedding song. Like I said on another previous post: POETRY! PURE POETRY!
PD: For those who are interested, the code-example of this entry it’s available and can be downloaded from my Github account. Enjoy it! :D
PD 2: I must apologize to the people who has downloaded the example code to view and test it. I must admit that there were some errors that didn’t allow its correct execution. I have already fixed it, so now it must work without problems. Greetings and apologies for any inconvenience.


