如何在多个实体管理器中使用 Symfony 自动装配

How to use Symfony autowiring with multiple entity managers

我想在使用 2 个不同实体管理器的服务中使用自动装配。如何实现这样的目标?

use Doctrine\ORM\EntityManager;
class TestService
{
    public function __construct(EntityManager $emA, EntityManager $emB)
    {
    }
}

我的 service.yml 文件是这样配置的:

    app.testservice:
        class: App\Services\TestService
        arguments:
            - "@doctrine.orm.default_entity_manager"
            - "@doctrine.orm.secondary_entity_manager"

最简单的方法是在你的构造函数中自动装配 ManagerRegistry 并通过使用你在配置文件中设置的实体管理器的名称来使用它来获取你想要的管理器(doctrine.yaml) :

use Doctrine\Common\Persistence\ManagerRegistry;
class TestService
{

    private $emA;
    private $emB;
    public function __construct(ManagerRegistry $doctrine)
    {
         $this->emA = $doctrine->getManager('emA');
         $this->emB = $doctrine->getManager('emB');
    }
}

而且您应该可以随心所欲地使用它们。


另一种方法是遵循this answer by Ron Mikluscak

Dylan 的回答违反了得墨忒尔定律的原则。自 Symfony 3.4 以来,它非常简单和优雅,满足 Local service binding:

services:
  _defaults:
    bind:
      $emA: "@doctrine.orm.default_entity_manager"
      $emB: "@doctrine.orm.secondary_entity_manager"

然后在您的服务中,自动加载将为您完成艰苦的工作:

class TestService
{
    public function __construct(EntityManager $emA, EntityManager $emB)
    {
        …
    }
}

已经发布了两个很好的答案,但我想添加第三个以及一些上下文以帮助选择在给定情况下使用哪种方法。

emix 的回答非常简单但有点脆弱,因为它依赖于参数名称来注入正确的服务。这很好,但您不会从 IDE 那里得到任何帮助,有时可能会有点尴尬。答案可能应该使用 EntityManagerInterface 但这是一个小问题。

DynlanKas 的回答要求在每个服务中使用一些代码来定位所需的管理器。没关系,但可能有点重复。另一方面,当您事先不知道确切需要哪位经理时,答案是完美的。它允许您 select 基于一些动态信息的经理。

第三个答案主要基于 Ron's Answer,但稍加改进。

为每个实体管理器创建一个新的 class:

namespace App\EntityManager;
use Doctrine\ORM\Decorator\EntityManagerDecorator;
class AEntityManager extends EntityManagerDecorator {}
class BEntityManager extends EntityManagerDecorator {}

不要担心您正在扩展装饰器 class。 class 与 'real' 实体管理器具有相同的界面和相同的功能。您只需要注入所需的管理器:

# config/services.yaml
App\EntityManager\AEntityManager:
    decorates: doctrine.orm.a_entity_manager

App\EntityManager\BEntityManager:
    decorates: doctrine.orm.b_entity_manager

这种方法需要为每个实体管理器创建一个新的 class 以及几行配置,但允许您简单地针对所需的 class:

输入提示
public function __construct(AEntityManager $emA, BEntityManager $emB)
{
}

可以说,这是处理原始问题的最可靠和最标准的方法。

只需使用EntityManagerInterface $secondaryEntityManager

如果您正在使用 Symfony 的框架包(我很确定您是),那么 Symfony >= 4.4 会自动为每个 Entitymanger 生成驼峰 autowiring aliases你定义。

您可以使用 debug:autowiring 控制台命令简单地获取它们的列表。对于上面的配置,这应该看起来像这样:

bin/console debug:autowiring EntityManagerInterface

Autowirable Types
=================

 The following classes & interfaces can be used as type-hints when autowiring:
 (only showing classes/interfaces matching EntityManagerInterface)

 EntityManager interface
 Doctrine\ORM\EntityManagerInterface (doctrine.orm.default_entity_manager)
 Doctrine\ORM\EntityManagerInterface $defaultEntityManager (doctrine.orm.default_entity_manager)
 Doctrine\ORM\EntityManagerInterface $secondaryEntityManager (doctrine.orm.secondary_entity_manager)

https://symfony.com/doc/4.4/doctrine/multiple_entity_managers.html所述:

Entity managers also benefit from autowiring aliases when the framework bundle is used. For example, to inject the customer entity manager, type-hint your method with EntityManagerInterface $customerEntityManager.

所以你应该只需要:

use Doctrine\ORM\EntityManagerInterface;

class TestService
{
  public function __construct(
    EntityManagerInterface $defaultEntityManager,
    EntityManagerInterface $secondaryEntityManager
  ) {
    // ...
  }
}

名称 $defaultEntityManager 不是强制性的,但它有助于区分两者。每个带有 EntityManagerInterface 类型提示且不在 debug:autowiring EntityManagerInterface 返回的列表中的参数将导致默认的 Entitymanager 被自动装配。

注意:如文档中所写和 debug:autowiring 的输出中所示,您需要使用 EntityManagerInterface 作为别名自动装配,而不是实际的 EntityManager class.事实上,您应该始终使用 EntityManagerInterface.

自动装配 Entitymanager