简体   繁体   English

使用自定义Doctrine 2水化器进行依赖注入

[英]Dependency injection with custom Doctrine 2 hydrator

I'm setting up a custom hydrator in Doctrine 2 in a Symfony 2 project, but for it to do what it needs it requires another service. 我在Symfony 2项目的Doctrine 2中设置了一个自定义水龙头,但是要使其完成所需的工作,则需要另一项服务。 The documentation for custom hydrators only shows how to provide a hydrator class, so there's no way to inject dependencies. 自定义水龙头文档仅显示如何提供水龙头类,因此无法注入依赖项。

For example: 例如:

$em->getConfiguration()->addCustomHydrationMode('CustomHydrator', 'MyProject\Hydrators\CustomHydrator');

I suspect Doctrine is initialising the hydrators itself and as such any dependencies would need to be passed through some other Doctrine classes first. 我怀疑Doctrine正在初始化水化器本身,因此任何依赖项都需要首先通过其他Doctrine类进行传递。

Is there a way to provide a custom "hydration factory" or similar to Doctrine that would allow injection of additional dependencies? 有没有提供自定义“水化工厂”或类似于Doctrine的方式来允许注入其他依赖项的方法? Custom hydrators seem fairly limited without this capability. 没有此功能的定制水龙头似乎相当有限。


Answer: Thanks to Denis V 答:感谢Denis V

I got this working as follows. 我按如下方式工作。 I can't post the actual code so I've put together some dummy placeholders so you can see how it fits together. 我无法发布实际的代码,因此我将一些虚拟占位符放在一起,以便您了解它们如何组合在一起。

src/Acme/ExampleBundle/resources/config/services.yml src / Acme / ExampleBundle / resources / config / services.yml

services:
    doctrine.orm.entity_manager.abstract:
        class:          Acme\ExampleBundle\Entity\DoctrineEntityManager
        factory_class:  Acme\ExampleBundle\Entity\DoctrineEntityManager
        factory_method: create
        abstract:       true
        calls:
            - [ setMyDependency, [@acme.my_custom_service]]

src/Acme/ExampleBundle/Entity/DoctrineEntityManager.php src / Acme / ExampleBundle / Entity / DoctrineEntityManager.php

namespace Acme\ExampleBundle\Entity;

use Acme\ExampleBundle\Hydrator\MyHydrator;
use Doctrine\Common\EventManager;
use Doctrine\DBAL\Connection;
use Doctrine\ORM\Configuration;
use Doctrine\ORM\EntityManager as BaseEntityManager;
use Doctrine\ORM\ORMException;
use Doctrine\ORM\Query;

class DoctrineEntityManager extends BaseEntityManager
{
    protected $myDependency;

    /**
     * Note: This must be redefined as Doctrine's own entity manager has its own class name hardcoded in.
     */
    public static function create($conn, Configuration $config, EventManager $eventManager = null)
    {
        if (!$config->getMetadataDriverImpl()) {
            throw ORMException::missingMappingDriverImpl();
        }

        switch (true) {
            case (is_array($conn)):
                $conn = \Doctrine\DBAL\DriverManager::getConnection(
                    $conn, $config, ($eventManager ?: new EventManager())
                );
                break;

            case ($conn instanceof Connection):
                if ($eventManager !== null && $conn->getEventManager() !== $eventManager) {
                     throw ORMException::mismatchedEventManager();
                }
                break;

            default:
                throw new \InvalidArgumentException("Invalid argument: " . $conn);
        }

        return new self($conn, $config, $conn->getEventManager());
    }

    public function setMyDependency($myCustomService)
    {
        $this->myDependency = $myCustomService;
    }

    public function newHydrator($hydrationMode)
    {
        if ($hydrationMode == 'MyHydrationMode') {
            return new MyHydrator($this, $this->myDependency);
        }

        return parent::newHydrator($hydrationMode);
    }
}

src/Acme/ExampleBundle/Hydrator/MyHydrator.php src / Acme / ExampleBundle / Hydrator / MyHydrator.php

namespace Acme\ExampleBundle\Hydrator;

use Doctrine\ORM\EntityManager;
use Doctrine\ORM\Internal\Hydration\ObjectHydrator;

class MyHydrator extends ObjectHydrator
{
    protected $myDependency;

    public __construct(EntityManager $em, $myDependency)
    {
        parent::__construct($em);

        $this->myDependency = $myDependency;
    }

    protected function hydrateAllData()
    {
        /* hydration stuff with my dependency here */
    }
}

Try adding this in your config.yml 尝试将其添加到您的config.yml中

doctrine:
    orm:
        hydrators:
            CustomHydrator: MyProject\Hydrators\CustomHydrator

UPDATE 更新

As you cannot inject anything to the Hydrator itself, you can instead create a custom EntityManager (that you suggested yourself). 由于您无法向Hydrator本身注入任何内容,因此可以创建一个自定义的EntityManager(由您自己建议)。

It can be done this way: 可以通过以下方式完成:

services:  
    name_of_your_custom_manager:
        class: %doctrine.orm.entity_manager.class%
        factory_service:  doctrine
        factory_method:   getManager
        arguments: ["name_of_your_custom_manager"]
        calls:
            - [ setCustomDependency, ["@acme_bundle.custom_dependency"] ]

Really good answer but please be advised that Doctrine maintainers explicitly said not to extend Doctrine\\ORM\\EntityManager and I suppose that in the future they will make it final to enforce this. 确实是一个很好的答案,但请注意,Doctrine维护者明确表示不要扩展Doctrine \\ ORM \\ EntityManager,并且我认为将来他们将最终强制执行此操作。

So instead of the proposed solution without breaking rules here is the cleaner solution: 因此,这里有一个更清洁的解决方案,而不是不违反规则的建议解决方案:

<?php

declare(strict_types=1);

namespace App\Doctrine\ORM;

use Doctrine\Common\EventManager;
use Doctrine\DBAL\Connection;
use Doctrine\ORM\Configuration;
use Doctrine\ORM\Decorator\EntityManagerDecorator;
use Doctrine\ORM\EntityManagerInterface;
use Doctrine\ORM\ORMException;

class EntityManager extends EntityManagerDecorator
{
    public function __construct(EntityManagerInterface $wrapped)
    {
        parent::__construct($wrapped);
    }

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

        switch (true) {
            case (is_array($conn)):
                $conn = \Doctrine\DBAL\DriverManager::getConnection(
                    $conn, $config, ($eventManager ?: new EventManager())
                );
                break;

            case ($conn instanceof Connection):
                if ($eventManager !== null && $conn->getEventManager() !== $eventManager) {
                    throw ORMException::mismatchedEventManager();
                }
                break;

            default:
                throw new \InvalidArgumentException("Invalid argument: " . $conn);
        }

        return new EntityManager($conn, $config, $conn->getEventManager());
    }
}

And now define this service in your services.xml file as decorating the desired entity manager: 现在,在您的services.xml文件中将此服务定义为装饰所需的实体管理器:

<?xml version="1.0" encoding="UTF-8" ?>
<container xmlns="http://symfony.com/schema/dic/services"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://symfony.com/schema/dic/services http://symfony.com/schema/dic/services/services-1.0.xsd">


    <services>
        <defaults autowire="true" autoconfigure="true" public="false" />

        <service
            id="decorated.doctrine.orm.default_entity_manager"
            class="App\Doctrine\ORM\EntityManager"
            decorates="doctrine.orm.default_entity_manager"
        >
            <argument type="service" id="decorated.doctrine.orm.default_entity_manager.inner" />
        </service>

    </services>
</container>

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM