Symfony: Costringere un'entità ad utilizzare una connessione al database diversa da quella predefinita configurata

Non è insolito avere più database per un singolo progetto. Normalmente, quando si utilizzano due database, le entità possono essere organizzate in specifiche cartelle che sono dichiarate nella tua configurazione. In questo caso, il sistema utilizzerà la connessione configurata per la directory dell'entità. Puoi vedere un esempio di connessioni multiple qui Symfony 4 / Sonata: gestire un'interfaccia di amministrazione multi-server

Supponiamo che vogliamo puntare a un database in sola lettura (una replica) ogni volta che utilizziamo un'entità. Ci sarebbero diversi modi di procedere.

Iniziamo impostando più configurazioni.

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

Per puntare a una connessione specifica, potremmo modificare il nostro codice e puntare alla nostra connessione per ogni richiesta Doctrine.

Ecco già due metodi per puntare al nostro server slave per ogni richiesta:

<?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();
        }
}

O ancora:

<?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();
        }
}

Ma ciò implica modificare tutti i nostri metodi uno per uno per puntare alla nostra connessione.

Un metodo più globale consiste nel definire la nostra connessione nel nostro costruttore per l'intero 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();
        }
}


Alla fine, puoi scegliere tra i diversi metodi disponibili secondo le esigenze della tua applicazione.