Doctrine caching in DotKernel

Using an ORM in production without any sort of cache strategy is a very bad move. The Database server chokes; lots of CPU cycles wasted only to generate metadata and queries over and over again at each request; the system slows down and so on.

Following Doctrine documentation , we choose to configure doctrine cache system through psr/container.

The main parts we are going to use are query_cache, metadata_cache and result_cache.

As cache type we choose PhpFileCache and for result cache we are setting a lifetime of 3600 seconds.

Configuration

In the file: config/autoload/doctrine.global.php add the below entry:

'doctrine' => [
    'cache' => [
            \Doctrine\Common\Cache\PhpFileCache::class => [
                'class' => \Doctrine\Common\Cache\PhpFileCache::class,
                'directory' => getcwd() . '/data/cache/doctrine'
            ]
        ]
    ],
'resultCacheLifetime' => 3600

In the file: config/autoload/local.php add the below entry in the doctrine section:

    'configuration' => [
        'orm_default' => [
            // it is recommended to disable doctrine cache on development
            // just comment any type of cache you dont want to be applied on development
            'query_cache' => \Doctrine\Common\Cache\PhpFileCache::class,
            'metadata_cache' => \Doctrine\Common\Cache\PhpFileCache::class,
            'result_cache' => \Doctrine\Common\Cache\PhpFileCache::class
        ]
    ],

Metadata Cache

Your class metadata is being parsed on each request. Instead of parsing this information we should cache it using one of the cache drivers.
We enabled this by adding the following metadata_cache key to our doctrine configuration:

'doctrine' => [
    'configuration' => [
            'orm_default' => [
                'metadata_cache' => \Doctrine\Common\Cache\PhpFileCache::class,
            ]
        ]
    ]

Query Cache

It is highly recommended that in a production environment you cache the transformation of a DQL query to its SQL counterpart.
It doesn’t make sense to do this parsing multiple times as it doesn’t change unless you alter the DQL query.
We enabled this by adding the following query_cache key to our doctrine configuration:

'doctrine' => [
    'configuration' => [
            'orm_default' => [
                'query_cache' => \Doctrine\Common\Cache\PhpFileCache::class,
            ]
        ]
    ]

Usage

$query = $em->createQuery('select u from \Entities\User u');
$query->useQueryCache(true);

Result Cache

The result cache can be used to cache the results of your queries so that doctrine don’t have to query the database or hydrate the data again after the first time.
We enabled this by adding the following result_cache key to our doctrine configuration:

'doctrine' => [
    'configuration' => [
            'orm_default' => [
                'result_cache' => \Doctrine\Common\Cache\PhpFileCache::class,
            ]
        ]
    ]

Usage

$query = $em->createQuery('select u from \Entities\User u');
$query->enableResultCache();

Note: You can set a lifetime for result cache to live before it will be rewrite by simply passing the value as the first argument:

$query->enableResultCache( $resultCacheLifetime);

Note: You can set a custom ID for the result cache which is automatically generated for you if you don’t set a custom ID yourself:

$query->enableResultCache( $resultCacheLifetime, 'my_custom_id');

Dotkernel Admin Example

Below there is the code used in Dotkernel Admin in order to list all Admins.

Is using both query_cache and result_cache.

$qb = $this->getEntityManager()->createQueryBuilder();
$qb->select('admin')
->from(Admin::class, 'admin');

if (!is_null($search)) {
$qb->where($qb->expr()->like('admin.identity', ':search'))
->setParameter('search', '%' . $search . '%');
}

$qb->setFirstResult($offset)
->setMaxResults($limit);
$qb->orderBy('admin.' . $sort, $order);

return $qb->getQuery()->useQueryCache(true)->enableResultCache($this->getCacheLifetime())->getResult();

How to return Collections

For returning collections that extends Doctrine Paginator (Doctrine\ORM\Tools\Pagination\Paginator) just enable result and/or query cache before passing query builder to collection

$qb = $this->getEntityManager()->createQueryBuilder();
$qb->select('collection')
->from(MyCollection::class, 'collection');

$qb->setFirstResult($offset)
->setMaxResults($limit);

$qb->getQuery()->enableResultCache($this->getCacheLifetime())->useQueryCache(true);

return new MyCollection($qb);

and the MyCollection look like:

<?php

use Doctrine\ORM\Tools\Pagination\Paginator;

/**
* Class MyCollection
* @package Core\Collection
*/
class MyCollection extends Paginator
{
}

References

Leave a Reply

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>