Il n’est pas rare d’avoir plusieurs bases de données pour un même projet. Normalement, lorsque 2 bases de données sont utilisées les entités peuvent être rangées dans des répertoires spécifiques qui sont déclarés dans votre configuration. Et dans ce cas le système va utiliser la connexion configurée pour le répertoire de l’entité. Vous pouvez voir un exemple de connexion multiple ici Symfony 4 / Sonata : gérer une interface d’admin multi serveur
Admettons que nous voulions juste pointer sur une base de données en lecture seule (une réplication), à chaque fois que nous utilisons une entité. Nous aurions plusieurs manières de procéder.
Commençons par poser une configuration multiple.
DATABASE_MASTER=mysql://login:password@db_master.host:3306/db_master?serverVersion=mariadb-10.10.2
DATABASE_SLAVE=mysql://login:password@db_slave.host:3306/db_slave?serverVersion=mariadb-10.10.2
doctrine:
dbal:
default_connection: db_master
types:
json: Sonata\Doctrine\Types\JsonType
connections:
db_master:
url: '%env(resolve:DATABASE_MASTER)%'
driver: 'pdo_mysql'
server_version: '5.7'
charset: utf8
default_table_options:
charset: utf8mb4
collate: utf8mb4_unicode_ci
db_slave:
url: '%env(resolve:DATABASE_SLAVE)%'
driver: 'pdo_mysql'
server_version: '5.7'
charset: utf8
default_table_options:
charset: utf8mb4
collate: utf8mb4_unicode_ci
orm:
auto_generate_proxy_classes: true
default_entity_manager: default
entity_managers:
default:
connection: db_master
naming_strategy: doctrine.orm.naming_strategy.underscore
auto_mapping: true
mappings:
ApplicationSonataMediaBundle: ~
SonataMediaBundle: ~
Main:
is_bundle: false
type: annotation
dir: '%kernel.project_dir%/src/Entity'
prefix: 'App\Entity\'
alias: Main
slave:
connection: db_slave
naming_strategy: doctrine.orm.naming_strategy.underscore
auto_mapping: false
mappings:
delef:
is_bundle: false
type: annotation
dir: '%kernel.project_dir%/src/Entity'
prefix: 'App\Entity'
alias: delef
Pour pointer sur une connexion spécifique, nous pourrions reprendre notre code et pointer sur notre connexion pour chaque requête doctrine.
Voici déjà 2 méthodes pour pointer sur notre serveur slave à chaque requête :
<?php
namespace App\Repository;
use App\Entity\Whatever;
use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository;
use Doctrine\ORM\EntityRepository;
use Psr\Container\ContainerInterface;
class WhateverRepository extends ServiceEntityRepository
{
public function __construct(ContainerInterface $container)
{
$this->container = $container;
}
public function selectSlave()
{
return $this->container->get('doctrine')
->getManager('slave')
->createQueryBuilder('w')
->andWhere('w.exampleField = :val')
->setParameter('val', $value)
->orderBy('w.id', 'ASC')
->setMaxResults(10)
->getQuery()
->getResult();
}
}
Ou bien encore :
<?php
namespace App\Repository;
use App\Entity\Whatever;
use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository;
use Doctrine\ORM\EntityRepository;
use Doctrine\Common\Persistence\ManagerRegistry;
class WhateverRepository extends ServiceEntityRepository
{
public function __construct(ManagerRegistry $registry)
{
$this->registry = $registry;
}
public function selectSlave()
{
return $this->registry->
->getManager('slave')
->createQueryBuilder('w')
->andWhere('w.exampleField = :val')
->setParameter('val', $value)
->orderBy('w.id', 'ASC')
->setMaxResults(10)
->getQuery()
->getResult();
}
}
Mais cela implique de reprendre toutes nos méthodes une à une pour pointer sur notre connexion.
Une méthode plus globale consiste à définir notre connexion dans notre constructeur pour tout notre repository.
<?php
namespace App\Repository;
use App\Entity\Whatever;
use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository;
use Doctrine\ORM\EntityRepository;
use Doctrine\Common\Persistence\ManagerRegistry;
use Doctrine\ORM\EntityRepository;
class WhateverRepository extends ServiceEntityRepository
{
public function __construct(ManagerRegistry $registry)
{
$this->registry = $registry;
$manager = $registry->getManager('slave');
parent::__construct($registry, Whatever::class);
EntityRepository::__construct(
$manager,
$manager->getClassMetadata(Whatever::class)
);
}
public function selectSlave()
{
return $this->createQueryBuilder('w')
->andWhere('w.exampleField = :val')
->setParameter('val', $value)
->orderBy('w.id', 'ASC')
->setMaxResults(10)
->getQuery()
->getResult();
}
}
Au final vous pourrez piocher à votre convenance dans les différentes méthodes possibles selon les besoins de votre application.