Comment utiliser le cache de Doctrine DBAL sur Symfony2

Sur Symfony, c’est DoctrineBundle qui est responsable de la configuration de Doctrine, et donc de la partie DBAL. Néamoins, le bundle ne permet pas encore de configurer le cache pour DBAL (tandis qu’il le permet pour l’ORM). Il vous faudra donc modifier la définition du service DBAL pour y configurer un driver de cache (memcached, apc, ou autre).

Vous devez donc créer un CompilerPass, qui sera chargé depuis l’un de vos propre bundle, et qui aura pour rôle de récupérer la définition du service dbal et de lui ajouter la configuration nécessaire à l’utilisation du cache.

<?php
// src/Acme/HelloBundle/DependencyInjection/Compiler/AddDbalCacheConfigurationPass.php

namespace Acme\HelloBundle\DependencyInjection\Compiler;

use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Reference;

class AddDbalCacheConfigurationPass implements CompilerPassInterface
{
	public function process(ContainerBuilder $container)
	{
		$id = 'doctrine.dbal.default_connection.configuration';

		if ($container->hasDefinition($id)) {
			$container
				->getDefinition($id)
					->addMethodCall('setResultCacheImpl', array(new Reference('doctrine.orm.default_result_cache')))
			;
		}
	}
}

Comme vous pouvez le voir dans la documentation de Doctrine, pour pouvoir utiliser le cache avec DBAL, vous devez utiliser la méthode setResultCacheImpl de l’objet de Configuration de la connexion DBAL, et lui spécifier le driver de cache que vous souhaitez utiliser. Dans notre cas, nous lui donnons le nom d’un service que Doctrine créé à la base pour la configuration du cache de l’ORM. C’est une petite astuce permettant de simplifier la chose.

Donc pour configurer le driver de cache que vous souhaitez utiliser, c’est exactement de la même manière que pour l’ORM :

// app/config/config.yml

doctrine:
    orm:
        query_cache_driver: apc

Ensuite, vous ajoutez votre CompilerPass au ContainerBuilder dans la classe principale de votre bundle :

<?php
// src/Acme/HelloBundle/AcmeHelloBundle.php

namespace Acme\HelloBundle;

use Symfony\Component\HttpKernel\Bundle\Bundle;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Acme\HelloBundle\DependencyInjection\Compiler\AddDbalCacheConfigurationPass;

class AcmeHelloBundle extends Bundle
{
	public function build(ContainerBuilder $container)
	{
		parent::build($container);
	
		$container->addCompilerPass(new AddDbalCacheConfigurationPass());
	}
}

Pour finir, il ne vous reste plus qu’à faire une requête DBAL et lui dire d’utiliser le cache :

<?php
// src/Acme/HelloBundle/Repository/MyEntityRepository.php

// ...
		$lifetime = 1800;
		$params = array('myParam' => 'myValue');
		$conn = $this->getEntityManager()->getConnection();
		$qb = $conn->createQueryBuilder();
		$qb
			->select('name')
			->from('myTable', 't')
			->where('something = :myParam')
		;

		$query = $qb->getSql();
		$stmt = $conn->executeQuery(
			$query,
			$params,
			array(\Doctrine\DBAL\Connection::PARAM_STR_ARRAY),
			new QueryCacheProfile($lifetime, 'my_cache_key')
		);
		
		$result = $stmt->fetchAll(\PDO::FETCH_ASSOC);	
		$stmt->closeCursor(); // very important, do not forget
// ...

(Versions utilisées : 2.3)