Symfony 将依赖项注入实体存储库

Symfony 将依赖项注入实体存储库,symfony,doctrine-orm,Symfony,Doctrine Orm,有没有一种简单的方法可以将依赖项注入Doctrine2中的每个存储库实例 我曾尝试侦听loadClassMetadata事件并在存储库上使用setter注入,但这自然会导致无限循环,因为在事件中调用getRepository触发了相同的事件 在查看了条令\ORM\EntityManager::getRepository方法之后,似乎存储库根本没有使用依赖注入,而是在功能级别实例化: public function getRepository($entityName) { $entityN

有没有一种简单的方法可以将依赖项注入Doctrine2中的每个存储库实例

我曾尝试侦听
loadClassMetadata
事件并在存储库上使用setter注入,但这自然会导致无限循环,因为在事件中调用
getRepository
触发了相同的事件

在查看了
条令\ORM\EntityManager::getRepository
方法之后,似乎存储库根本没有使用依赖注入,而是在功能级别实例化:

public function getRepository($entityName)
{
    $entityName = ltrim($entityName, '\\');
    if (isset($this->repositories[$entityName])) {
        return $this->repositories[$entityName];
    }

    $metadata = $this->getClassMetadata($entityName);
    $customRepositoryClassName = $metadata->customRepositoryClassName;

    if ($customRepositoryClassName !== null) {
        $repository = new $customRepositoryClassName($this, $metadata);
    } else {
        $repository = new EntityRepository($this, $metadata);
    }

    $this->repositories[$entityName] = $repository;

    return $repository;
}

有什么想法吗?

如果您使用自定义EntityManager,您可以覆盖
getRepository
方法。由于这不涉及
loadClassMetadata
事件,因此不会运行无限循环

首先必须将依赖项传递给自定义EntityManager,然后使用setter注入将其传递给存储库对象

我回答了如何使用自定义EntityManager,但我将复制以下答案:

1-覆盖
条令.orm.entity\u manager.class
参数以指向您的自定义实体管理器(应扩展
条令\orm\EntityManager

2-自定义实体管理器必须重写
create
方法,以便它返回类的实例。请参阅下面的示例,并注意关于MyEntityManager的最后一行:

public static function create($conn, Configuration $config, EventManager $eventManager = null) {
        if (!$config->getMetadataDriverImpl()) {
            throw ORMException::missingMappingDriverImpl();
        }

        if (is_array($conn)) {
            $conn = \Doctrine\DBAL\DriverManager::getConnection($conn, $config, ($eventManager ? : new EventManager()));
        } else if ($conn instanceof Connection) {
            if ($eventManager !== null && $conn->getEventManager() !== $eventManager) {
                throw ORMException::mismatchedEventManager();
            }
        } else {
            throw new \InvalidArgumentException("Invalid argument: " . $conn);
        }

        // This is where you return an instance of your custom class!
        return new MyEntityManager($conn, $config, $conn->getEventManager());
    }
你还需要在课堂上使用以下内容:

use Doctrine\ORM\EntityManager;
use Doctrine\ORM\Configuration;
use Doctrine\ORM\ORMException;
use Doctrine\Common\EventManager;
use Doctrine\DBAL\Connection;
编辑

由于默认的实体管理器是通过
create
方法创建的,因此不能简单地向其中注入服务。但是,由于您正在创建一个自定义实体管理器,因此可以将其连接到服务容器并注入所需的任何依赖项

然后,在覆盖的
getRepository
方法中,您可以执行类似

$repository->setFoo($this->foo)
。这是一个非常简单的示例-在调用它之前,您可能需要首先检查
$repository
是否有
setFoo
方法。实现取决于您,但这展示了如何使用setter注入来实现存储库。

问题是存储库类不是Symfony2代码库的一部分,因为它们是Doctrine2的一部分,所以它们不会利用DIC;这就是为什么不能在一个地方对所有存储库进行注入

我建议你使用不同的方法。例如,您可以在存储库的顶部创建一个服务层,并通过该层中的工厂实际注入所需的类

否则,您也可以通过以下方式将存储库定义为服务:

<service id="your_namespace.repository.repos_name"
          class="%your_namespace.repository.repos_name%"
          factory-service="doctrine" factory-method="getRepository">
  <argument>entity_name</argument>
  <argument>entity_manager_name</argument>
  <call method="yourSetter">
      <argument>your_argument</argument>
  </call>
</service>

实体名称
实体经理姓名
你的论点

可以集中set方法调用的解决方案是编写一个DIC标记和一个编译器过程来处理它,并标记所有存储库服务。

这是Aldo答案的YAML版本,以防您使用YAML配置而不是XML

您的_namespace.repository.repos_名称:
类:%u名称空间。存储库。repos\u名称%
工厂:[“@doctrine”,getRepository]
论据:
-实体名称
-实体经理姓名
电话:
-[setContainer,[“@service_container”]]
在版本2.8之前:

您的_namespace.repository.repos_名称:
类:%u名称空间。存储库。repos\u名称%
工厂服务:原则
工厂方法:getRepository
论据:
-实体名称
-实体经理姓名
电话:
-[setContainer,[@service_container]]

另外,请注意,实体\u管理器\u名称是一个可选参数。我想为我的特定用途使用默认值,所以我将其留空(以防我重命名默认管理器)。

我刚刚定义了自己的RepositoryFactory类

  • 创建RepositoryFactory类并定义服务,例如带有include@service\u容器注入的
    my\u service.orm\u repository.robo\u repository\u factory
  • 并添加检查和设置容器服务,例如:

    private function createRepository(EntityManagerInterface $entityManager, $entityName)
    {
        /* @var $metadata \Doctrine\ORM\Mapping\ClassMetadata */
        $metadata = $entityManager->getClassMetadata($entityName);
        $repositoryClassName = $metadata->customRepositoryClassName
            ?: $entityManager->getConfiguration()->getDefaultRepositoryClassName();
    
        $result = new $repositoryClassName($entityManager, $metadata);
        if ($result instanceof ContainerAwareInterface) {
            $result->setContainer($this->container);
        }
        return $result;
    }
    
  • 创建编译器类

    public function process(ContainerBuilder $container)
    {
        $def = $container->getDefinition('doctrine.orm.configuration');
        $def->addMethodCall(
            'setRepositoryFactory', [new Reference('robo_doctrine.orm_repository.robo_repository_factory')]
        );
    }
    
  • 之后,任何
    EntityRepository
    containerWareInterface
    具有@service\u container


  • 实际上,您可以创建自己的
    DefaultRepository extensed EntityRepository
    ,使用所需的所有依赖项构建它,然后使用以下各项将其设置为默认
    存储库

    doctrine:
        orm:
            entity_managers:
                default:
                    default_repository_class: AppBundle\ORM\DefaultRepository
    

    自Symfony 3.3+和2017年以来您可以使用服务


    而不是此处提出的导致以下结果的其他解决方案:

    • 黑客入侵存储库工厂
    • 在YAML中进行服务配置
    • 并创建了大量的样板代码,稍后将找到您
    你能做到的


    干净方式-通过构造函数注入的依赖性,就像在任何其他服务中一样
    投票赞成这个很好的答案,但我选择了Arms答案,因为我发现它更容易实现。这是一个很棒的答案。我依靠的是一种温和的黑客方法,它在2.3版中停止了工作。我改用这种方法,效果很好。我在下面发布了一个相同的YAML格式,仅供参考。如何修改doctor.orm.entity\u manager.class服务来添加setter注入?我已经在
    vendor/symfony/src/symfony/Bundle/DoctrineBundle/Resources/config/orm.xml中查看了定义,但是我对如何添加注入有些困惑,因为我正在使用yml来定义我的服务我已经正确地设置了所有内容,但是当你说“您可以将其连接到服务容器并注入所需的任何依赖项”我在自定义EntityManager上有一个setContainer方法,我不知道使用什么配置来“连接”。我还对最后一个“连接到服务容器”步骤感到困惑。我也不确定如何“将其连接到服务容器”“-其他一切看起来都很好
    
    <?php declare(strict_types=1);
    
    namespace App\Repository;
    
    use App\Entity\Post;
    use Doctrine\ORM\EntityManager;
    use Doctrine\ORM\EntityRepository;
    
    final class PostRepository
    {
        /**
         * @var EntityRepository
         */
        private $repository;
    
        /**
         * @var YourOwnDependency
         */
        private $yourOwnDependency;
    
        public function __construct(YourOwnDependency $YourOwnDependency, EntityManager $entityManager)
        {
            $this->repository = $entityManager->getRepository(Post::class);
    
            $this->yourOwnDependency = $yourOwnDependency
        }
    }