Symfony: Bắt buộc một thực thể sử dụng kết nối cơ sở dữ liệu khác với kết nối mặc định đã cấu hình

Việc sử dụng nhiều cơ sở dữ liệu cho một dự án không phải là hiếm gặp. Thông thường, khi sử dụng hai cơ sở dữ liệu, các thực thể có thể được tổ chức vào các thư mục cụ thể được khai báo trong cấu hình của bạn. Trong trường hợp này, hệ thống sẽ sử dụng kết nối được cấu hình cho thư mục thực thể. Bạn có thể xem một ví dụ về nhiều kết nối tại đây Symfony 4 / Sonata: quản lý giao diện quản trị đa máy chủ

Giả sử chúng ta muốn chỉ định một cơ sở dữ liệu chỉ đọc (một bản sao) mỗi khi chúng ta sử dụng một thực thể. Sẽ có một số cách tiếp cận khác nhau.

Hãy bắt đầu bằng cách thiết lập nhiều cấu hình.

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

Để chỉ định một kết nối cụ thể, chúng ta có thể chỉnh sửa mã của mình và chỉ đến kết nối của chúng ta cho mỗi yêu cầu Doctrine.

Dưới đây là hai phương pháp để chỉ đến máy chủ phụ của chúng ta cho mỗi yêu cầu:

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

Hoặc lại:

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

Nhưng điều này đòi hỏi phải chỉnh sửa tất cả các phương thức của chúng ta từng cái một để chỉ đến kết nối của chúng ta.

Một phương pháp toàn cầu hơn bao gồm việc xác định kết nối của chúng ta trong bộ xây dựng của chúng ta cho toàn bộ kho lưu trữ của chúng ta.

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


Cuối cùng, bạn có thể lựa chọn từ các phương pháp khác nhau tùy theo nhu cầu của ứng dụng của bạn.