How to setup doctrine for zend expressive
NOTE: This post was written in the time there was no good solution available to add Doctrine to Expressive. Fortunately there is container-interop-doctrine. It makes setting up Doctrine a lot easier.
You want to use doctrine in your Zend Expressive project, but don't know where to start? Here is how to do it. This guide uses a doctrine factory and a separate cache factory, so you can use the doctrine cache for other purposes too.
Installing Doctrine
Thanks to composer, this is easy.
$ composer require doctrine/orm
Doctrine Factory
The factory reads the configuration and applies it to doctrine for you. It will return the entity manager.
<?php // src/App/Container/DoctrineFactory.php
namespace App\Container;
use Doctrine\Common\Annotations\AnnotationReader;
use Doctrine\Common\Annotations\AnnotationRegistry;
use Doctrine\Common\Cache\Cache;
use Doctrine\ORM\Configuration;
use Doctrine\ORM\EntityManager;
use Doctrine\ORM\Mapping\Driver\AnnotationDriver;
use Doctrine\ORM\Mapping\UnderscoreNamingStrategy;
use Interop\Container\ContainerInterface;
class DoctrineFactory
{
public function __invoke(ContainerInterface $container)
{
$config = $container->has('config') ? $container->get('config') : [];
$proxyDir = (isset($config['doctrine']['orm']['proxy_dir'])) ?
$config['doctrine']['orm']['proxy_dir'] : 'data/cache/EntityProxy';
$proxyNamespace = (isset($config['doctrine']['orm']['proxy_namespace'])) ?
$config['doctrine']['orm']['proxy_namespace'] : 'EntityProxy';
$autoGenerateProxyClasses = (isset($config['doctrine']['orm']['auto_generate_proxy_classes'])) ?
$config['doctrine']['orm']['auto_generate_proxy_classes'] : false;
$underscoreNamingStrategy = (isset($config['doctrine']['orm']['underscore_naming_strategy'])) ?
$config['doctrine']['orm']['underscore_naming_strategy'] : false;
// Doctrine ORM
$doctrine = new Configuration();
$doctrine->setProxyDir($proxyDir);
$doctrine->setProxyNamespace($proxyNamespace);
$doctrine->setAutoGenerateProxyClasses($autoGenerateProxyClasses);
if ($underscoreNamingStrategy) {
$doctrine->setNamingStrategy(new UnderscoreNamingStrategy());
}
// ORM mapping by Annotation
AnnotationRegistry::registerFile('vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/Driver/DoctrineAnnotations.php');
$driver = new AnnotationDriver(
new AnnotationReader(),
['src/Domain']
);
$doctrine->setMetadataDriverImpl($driver);
// Cache
$cache = $container->get(Cache::class);
$doctrine->setQueryCacheImpl($cache);
$doctrine->setResultCacheImpl($cache);
$doctrine->setMetadataCacheImpl($cache);
// EntityManager
return EntityManager::create($config['doctrine']['connection']['orm_default'], $doctrine);
}
}
The cache factory
The cache factory is a separate factory, so you can use it for other purposes as well. For this example, a Redis cache driver is used. You can easily create your own factory for other supported cache drivers.
<?php // src/App/Container/DoctrineRedisCacheFactory.php
namespace App\Container;
use Doctrine\Common\Cache\RedisCache;
use Interop\Container\ContainerInterface;
class DoctrineRedisCacheFactory
{
public function __invoke(ContainerInterface $container)
{
$config = $container->has('config') ? $container->get('config') : [];
$redis = new \Redis();
$redis->connect(
$config['doctrine']['cache']['redis']['host'],
$config['doctrine']['cache']['redis']['port']
);
$cache = new RedisCache();
$cache->setRedis($redis);
return $cache;
}
}
The configuration
You can go several ways to set up the configuration. You can create a doctrine.global.php
config file and overwrite
some settings in a local file. In this case the whole doctrine config is in the local config file since most settings
are different on the production server.
If you are using windows on your dev machine, make sure you use 127.0.0.1
as the host in stead of localhost
. It
massively speeds up connecting to the database. At least for MySQL, I don't know about other databases.
<?php // config/autoload/doctrine.local.php
return [
'doctrine' => [
'orm' => [
'auto_generate_proxy_classes' => false,
'proxy_dir' => 'data/cache/EntityProxy',
'proxy_namespace' => 'EntityProxy',
'underscore_naming_strategy' => true,
],
'connection' => [
// default connection
'orm_default' => [
'driver' => 'pdo_mysql',
'host' => '127.0.0.1',
'port' => '3306',
'dbname' => '*****',
'user' => '*****',
'password' => '*****',
'charset' => 'UTF8',
],
],
'cache' => [
'redis' => [
'host' => '127.0.0.1',
'port' => '6379',
],
],
],
];
The container
What is left is adding doctrine to the container, so you can easily access it throughout the project. To do that, you need to add 2 lines to your dependencies' config.
<?php // config/autoload/dependencies.global.php
return [
'dependencies' => [
'invokables' => [
// ...
],
'factories' => [
// ...
Doctrine\Common\Cache\Cache::class => App\Container\DoctrineRedisCacheFactory::class,
Doctrine\ORM\EntityManager::class => App\Container\DoctrineFactory::class,
],
],
];
Accessing doctrine
Everything is ready and registered in the container. To access doctrine, you need to get it from the container.
<?php // src/App/Action/IndexAction.php
namespace App\Action;
use Doctrine\ORM\EntityManager;
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\ServerRequestInterface;
use Zend\Diactoros\Response\HtmlResponse;
use Zend\Expressive\Template\TemplateRendererInterface;
class IndexAction
{
private $em;
private $template;
public function __construct(EntityManager $em, TemplateRendererInterface $template)
{
$this->em = $em;
$this->template = $template;
}
public function __invoke(ServerRequestInterface $request, ResponseInterface $response, callable $next = null)
{
$userRepository = $this->em->getRepository('App\Domain\User\User');
$users = $userRepository->findAll();
return new HtmlResponse($this->template->render('app::index'));
}
}
Using doctrine cache
Because the cache driver has been set up in its own factory, it can now be used whenever you need to cache something.
<?php
use Doctrine\Common\Cache\Cache;
/** @var Cache $cache */
$cache = $container->get(Cache::class);
if ($cache->contains('my_array')) {
// Fetching the cached data
$array = $cache->fetch('my_array');
} else {
// Example data
$array = array(
'key1' => 'value1',
'key2' => 'value2'
);
// Save data to the cache
$cache->save('my_array', $array);
}
More info on how to use the cache can be found in the doctrine docs.
Doctrine Console
The Doctrine Console is a very useful command line interface tool. However, it doesn't work out of the box. Doctine needs to know how to set up the entity manager with the right configuration. Fortunately, this is easily done by creating the file cli-config.php
in the config dir.
<?php // config/cli-config.php
use Doctrine\ORM\Tools\Console\ConsoleRunner;
use Doctrine\ORM\EntityManager;
require 'vendor/autoload.php';
/** @var \Interop\Container\ContainerInterface $container */
$container = require 'config/container.php';
/** @var \Doctrine\ORM\EntityManager $em */
$em = $container->get(EntityManager::class);
return ConsoleRunner::createHelperSet($em);
This file loads the configuration, set up the container and gets the entity manger which will then be injected in the console. All you need to do now is call the doctrine console.
# List all commands
$ php vendor/bin/doctrine list
More info about how to use the doctrine console can be found on the doctrine site.
Enjoy your zend expressive project with doctrine.