Astuces lorsqu'on travaille avec plusieurs connexions Doctrine dans un seul et même projet.
Travailler avec plusieurs bases de données dans un même projet n'arrive pas tous les jours.
Ce billet trace quelques astuces qui vous permettront probablement de gagner un peu de temps lorsque vous y serez confrontés.
C'est la première chose à faire lorsque vous utilisez plusieurs sources de données : dire à Doctrine sur quelle connexion il est censé mapper vos entités.
C'est ce que fait pour vous l'auto_mapping
lorsque vous n'avez qu'une seule base de données.
Toutes les entités d'un même bundle ne pourront être gérées que par une seule connexion. Une connexion peut gérer les entités de plusieurs bundles.
Structure des bundles et des entités :
AppBundle
> Entity
> Product.php
WarehouseBundle
> Entity
> Stock.php
doctrine:
dbal:
default_connection: default
connections:
default:
# ...
warehouse:
# ...
orm:
default_entity_manager: default
entity_managers:
default:
connection: default
mappings:
AppBundle: ~
external:
connection: warehouse
mappings:
WarehouseBundle: ~
Même si vous pouvez demander le manager en direct, en connaissant la façon dont l'id du service est construit par Symfony (doctrine.orm.{name}_entity_manager
), c'est une pratique fortement déconseillée, car elle ne tient pas compte de la configuration.
Le registre doctrine permet d'abstraire cette configuration.
<?php
namespace AppBundle\Controller;
use AppBundle\Entity\Product;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Warehouse\Entity\Stock;
class CartController extends Controller
{
public function indexAction()
{
$productManager = $this->getDoctrine()->getManagerForClass(Product::class);
$productRepository = $this->getDoctrine()->getRepository(Product::class);
$stockManager = $this->getDoctrine()->getManagerForClass(Stock::class);
$stockRepository = $this->getDoctrine()->getRepository(Stock::class);
}
}
Lorsque vous enregistrez un event listener/subscriber Doctrine, il est actif pour toutes les connections.
Ce qui peut poser problème si vous faites ce genre choses :
services:
app.event_listener.updated_at:
class: AppBundle\Event\Listener\UpdatedAtListener
tags:
- { name: doctrine.event_listener, event: onFlush }
<?php
namespace AppBundle\Event\Listener;
use AppBundle\Entity\Product;
use Doctrine\ORM\Event\OnFlushEventArgs;
class UpdatedAtListener
{
public function onFlush(OnFlushEventArgs $eventArgs)
{
$entityManager = $eventArgs->getEntityManager();
$products = $entityManager->getRepository(Product::class)->findAll();
}
}
<?php
$stock = $stockRepository->findForProduct($product);
$stock->setValue($stock->getValue() - 10);
$stockManager->flush();
Le service UpdatedAtListener
s'attend à faire quelque chose sur les produits lors du flush.
Mais il a été enregistré pour toutes les connexions.
Dans ce cas précis, $stockRepository
n'a aucune connaissance de l'entité Product
, une exception sera levée pour cela.
La solution est simple, il suffit de déclarer que l'event listener n'est valable que pour la connexion default
.
services:
app.event_listener.updated_at:
class: AppBundle\Event\Listener\UpdatedAtListener
tags:
- { name: doctrine.event_listener, event: onFlush, connection: default }