As not all of us are willing to let go ZF for Symfony, the native environment for Doctrine, here is a clean way of integrating Doctrine 2 into your ZF projects.
As the integration method chosen was to create an application resource, you may enable both new projects and existing ones to use Doctrine 2 without changing a single line of bootstrap code.
Added bonus – memcache integration and a custom Doctrine 2 eAccelerator caching class which I strongly advice against using, at least on Windows
Requirements:
- PHP 5.3
- ZF 1.10.5 or compatible
- Doctrine 2.0 beta 2 or compatible
- Memcache (optional, available on Windows too)
- Some of the attached source code
Initial setup:
Get ZF and Doctrine 2 and unpack them in the dedicated folders, in the library project folder (make sure you only have library/Zend, not library/Zend/Zend).
Move the folder Symfony from the Doctrine folder onto the one from library, overwriting it (I just thought it would be nice not to have multi layered library folders).
Leave out the tests, bins and other files from both ZF and Doctrine.
Copy the needed files from the attached source folder (library/Keops and the root doctrine files).
You will notice that in the attached project you will have some doctrine related files in the root of your project: these are files created based on the Doctrine 2 sandbox sample. They will allow you to use the very cool Doctrine command line (doctrine.bat) and help you propagate changes from your entities to the db.
The database connection information is hard coded in doctrine-cli-config.php too, besides application.ini – when I first integrated ZF and Doctrine 1 I have also updated the CLI tool to read its settings from application.ini; now I guess I was a bit lazy. Anyway, please feel free to do that.
Here is the code of the resource class, located in library/Keops/Application/Resource:
class Keops_Application_Resource_Doctrine2
extends Zend_Application_Resource_ResourceAbstract
{
public function init()
{
$bootstrapOptions = $this->getBootstrap()->getOptions();
$options = $this->getOptions();
$memcache = null;
$doctrineConfig = new \Doctrine\ORM\Configuration();
if (!empty($options['options']['metadataCache'])) {
$metaCache = new $options['options']['metadataCache']();
if ($metaCache instanceof
\Doctrine\Common\Cache\MemcacheCache) {
$memcache = new Memcache();
$memcache->connect('localhost', 11211);
$metaCache->setMemcache($memcache);
}
$doctrineConfig->setMetadataCacheImpl($metaCache);
}
if (!empty($options['options']['queryCache'])) {
$queryCache = new $options['options']['queryCache']();
if ($queryCache instanceof
\Doctrine\Common\Cache\MemcacheCache) {
if (is_null($memcache)) {
$memcache = new Memcache();
$memcache->connect('localhost', 11211);
}
$queryCache->setMemcache($memcache);
}
$doctrineConfig->setQueryCacheImpl($queryCache);
}
$driverImpl =
$doctrineConfig->newDefaultAnnotationDriver(
array($options['paths']['entities']));
$doctrineConfig->setMetadataDriverImpl($driverImpl);
//$doctrineConfig->setEntityNamespaces(
// $options['entitiesNamespaces']);
$doctrineConfig->setProxyDir($options['paths']['proxies']);
$doctrineConfig->setProxyNamespace(
$options['options']['proxiesNamespace']);
$this->getBootstrap()->em =
\Doctrine\ORM\EntityManager::create(
$options['connections']['doctrine'],
$doctrineConfig);
return $this->getBootstrap()->em;
}
}
As you may have probably noticed, the resource is configured to run on a memcache caching system. For testing, you may replace that with the included ArrayCache. For production you should definitely use a caching mechanism though. I don’t want to turn this into neither a ZF or a Doctrine tutorial, so let’s leave the subject.
All you need to do to make this work now is to add some settings to application.ini, enabling the new resource; add these to your [production] section:
pluginPaths.Keops_Application_Resource = APPLICATION_PATH "/../library/Keops/Application/Resource" autoloaderNamespaces[] = "Doctrine" autoloaderNamespaces[] = "Keops" autoloaderNamespaces[] = "Entities" resources.doctrine2.options.metadataCache = "Doctrine\Common\Cache\MemcacheCache" resources.doctrine2.options.queryCache = "Doctrine\Common\Cache\MemcacheCache" resources.doctrine2.options.proxiesNamespace = "Proxies" resources.doctrine2.paths.entities = APPLICATION_PATH "/doctrine/entities" resources.doctrine2.paths.proxies = APPLICATION_PATH "/doctrine/proxies" resources.doctrine2.connections.doctrine.driver = "pdo_mysql" resources.doctrine2.connections.doctrine.dbname = "eplaza" resources.doctrine2.connections.doctrine.user = "root" resources.doctrine2.connections.doctrine.password = "root"
As you can see, you can customize connection details, multiple database connections (just add more entries to resources.doctrine2.connections), separate caching for queries and metadata (the memcache PHP client will actually be shared) and the path to the entities and auto generated proxies. If you intend to change any of the paths, make sure the CLI tool is updated accordingly.
You are set up! All you need to do now is edit your entities from application/doctrine/entities like the one below:
namespace Entities;
/**
* @Entity
*/
class Store
{
/**
* @Id @Column(type="integer", name="store_id")
* @GeneratedValue
*/
protected $id;
/**
* @Column(length=32, nullable=TRUE)
*/
protected $ext_code;
/**
* @Column(length=64)
*/
protected $name;
/**
* @Column(length=8)
*/
protected $status;
/**
* @Column(type="datetime")
*/
protected $create_ts;
public function getId()
{
return $this->id;
}
public function getName()
{
return $this->name;
}
public function setName($name)
{
$this->name = $name;
}
public function getExtCode() {
return $this->ext_code;
}
public function setExtCode($ext_code) {
$this->ext_code = $ext_code;
}
public function getStatus() {
return $this->status;
}
public function setStatus($status) {
$this->status = $status;
}
public function getCreateTs() {
return $this->create_ts;
}
public function setCreateTs($create_ts) {
$this->create_ts = $create_ts;
}
}
Update the db mappings (this will update the db and create proxies in application/doctrine/proxies; don’t remember if you need to create the database before the first run):
doctrine-update.bat
And use your new Doctrine 2 ORM:
class IndexController extends Zend_Rest_Controller
{
public function indexAction()
{
$em = $this->getInvokeArg('bootstrap')->em;
$store = new \Entities\Store();
$store->setName('Paris');
$store->setStatus('A');
$store->setCreateTs(
new DateTime(Zend_Date::now()->get(Zend_Date::ISO_8601)));
$em->persist($store);
$em->flush();
echo $this->_helper->json(
array('success' => true, 'newRecordId' => $store->getId()));
}
}
In the root folder you will find also a doctrine-regen.bat file – this one will drop the schema and recreate all tables, so make sure you don’t have any data in the database that you will cry after. Usually you will use doctrine-update.bat, which shouldn’t delete anything.
I’ve had some issues putting it all together, especially with the whole Zend autoloader – Doctrine namespaces thing but it just seemed like a good direction to point my next projects to. Hope some of you will find this useful.
Peace!
can u post the base package whit zf doctrine complete?
If you mean a full project including the libraries, I didn’t want to go into the whole licensing territory. Anyway, the full package would be quite large.
It’s easy to setup though – I left the folder names, in library, as they should appear after copying the libraries.
I’ll try to post a library folder tree snaphot:
Thanks ! And what about the integration of ZFDebug in this config ?
Kind of misses the point of the tutorial
I haven’t had time to look into PHP for a while now. Maybe I’ll check it out
Nice article.
Did you try use YAML in doctrine?
I successfull used YAML in tools/sandbox configuration but when I tried with your integration with Zend I’ve got errors.
Any idea?
Regards.
I’ve always used YAML for Doctrine configuration. What kind of errors are you referring to?
First I’m using doctrine 2.0BETA3
I created directory in ./application/doctrine/yaml and put there yaml files
After that I changed doctrine-cli-config.php to load yaml driver $driverImpl like it was shown in documentation:
$driverImpl = new YamlDriver(array(__DIR__ . ‘/application/doctrine/yaml’));
When I’m trying to create database (doctrine.bat) I have this error
Fatal error: Class ‘YamlDriver’ not found in D:\Workspace\htdocs\doctrine2\doctrine-cli-config.php on line 22
When I try this way
$driverImpl = $config->newDefaultAnnotationDriver(array(__DIR__ . ‘/application/doctrine/yaml’));
I’ve got this ‘No Metadata Classes to process.’ and no database is created.
Earlier I was testing the just doctrine 2.0BETA3 with sandbox. With this lines after require_once works great
use Doctrine\ORM\EntityManager,
Doctrine\ORM\Configuration,
Doctrine\ORM\Mapping\Driver\YamlDriver;
but with your code I have another errors
Thanks for help.
Regards.
Ok, I made some research over Internet and saw that what I was trying to do will never works
Is it required to use command line tools to generate from yaml entities and than database etc.?
Regards.
Yes, use the command line tool to generate the DB structure, classes and sample data starting from YAML
Ok I’ve tried to determine the answer but for the life of me I can’t use the Entity Manager to query the database. Could you provide some examples of retrieving data from the db?
http://www.doctrine-project.org/projects/orm/2.0/docs/reference/introduction/en#using-an-object-relational-mapper
Did you try to work this with oracle db?
No, but it should work. That’s the whole point of DBALs and ORMs. Check out Doctrine’s documentation and forums if you run into any issues.
I’m trying to use xml and Entities class but I have problem during creating database with orm:schema-tool:create
xml files are in /application/doctrine/xml
Do you know how to configure cli-config.php to see xml and Entities folder?
Regards
I forgot to add. In sandbox its working fine.
Ok i figure it out
Sorry for bothering
Need to change in config
$classLoader = new \Doctrine\Common\ClassLoader('Entities', __DIR__ . '/application/doctrine');$classLoader->register();
and metadata driver should look like this
$driverImpl = new \Doctrine\ORM\Mapping\Driver\XmlDriver(array(__DIR__ . '/application/doctrine/xml'));$config->setMetadataDriverImpl($driverImpl);
regards
Glad you figgured it out
Running this on Apache + windows results in the following:
Warning: include_once(Entities\Store.php) [function.include-once]: failed to open stream: No such file or directory in C:\Program Files (x86)\Apache Software Foundation\Apache2.2\htdocs\demoproject\library\Zend\Loader.php on line 146
Is there some tweaking with paths that you need to do when running this on Windows?
Great!
It’s what I need for since a month. I’ll try it and let you know.
Best regard.
Hi
Is there a way to integrate Doctrine 2 and Zend with an existing database ?!
I mean I have the database up and running and need Doctrine to read my database schema and create the models for it.
I believe you could use the generate:entities command from the doctrine command line utility