【问题标题】:Is there a way to specify Doctrine2 Entitymanager implementation class in Symfony2?有没有办法在 Symfony2 中指定 Doctrine2 Entitymanager 实现类?
【发布时间】:2011-11-15 15:41:37
【问题描述】:

我目前正在使用 Symfony2 和 Doctrine2,但我必须覆盖 Doctrine2 EntityManager 并为其添加一些“取消删除”功能(ACL 内部)。

所以我想知道:有没有办法覆盖 EntityManager 类并在 Symfony2 中指定 Doctrine2 以将其用作 EntityManager 的实现?

感谢您的帮助!

【问题讨论】:

    标签: configuration symfony doctrine-orm


    【解决方案1】:

    在 Doctrine 2.4 (Doctrine 2.4 release) 之后,您需要为此使用装饰器。不要直接扩展 EntityManager。 首先,您需要实现自己的实体管理器装饰器,它扩展了 Doctrine\ORM\Decorator\EntityManagerDecorator (如@Dana) 但是你不能只将教义.orm.entity_manager.class 更改为你的新装饰器,因为 EntityManagerDecorator 在它的构造函数中需要 EntityManagerInterface:

    public function __construct(EntityManagerInterface $wrapped)
    

    您不能在这里只将教义.orm.entity_manager 作为参数传递,因为这将是一个递归。 不要这样做:

    return new self(\Doctrine\ORM\EntityManager::create(
    

    你需要在服务中配置你的装饰器,比如装饰器:

    yourcompany_entity_manager:
        public: false
        class: YourCompany\ORM\EntityManagerDecorator
        decorates: doctrine.orm.default_entity_manager
        arguments: ["@yourcompany_entity_manager.inner"]
    

    现在你将把你的装饰器作为 Doctrine 的默认实体管理器。 @yourcompany_entity_manager.inner 实际上是一个指向 doctrine.orm.default_entity_manager 的链接,它将被传递给 yourcompany_entity_manager 构造函数。

    用于配置装饰器的 Symfony 文档:link

    顺便说一句,这个命令对于调试你的服务非常有用:

    应用程序/控制台容器:调试 | grep entity_manager

    【讨论】:

    • 请注意,装饰器配置可从 Symfony 2.5 获得
    【解决方案2】:

    是的,分两步即可:

    1 - 覆盖 doctrine.orm.entity_manager.class 参数以指向您的自定义实体管理器(应扩展 Doctrine\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以下内容:

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

    说实话,我很惊讶第二步是必需的,我认为这应该可以仅使用服务容器来完成。

    【讨论】:

    • 非常感谢您的帮助 Arms!你能告诉我文档在哪里解释了如何覆盖实现吗?我要疯了,我在文档中找不到它!
    • 我得到了 2d 步骤:EntityManager 是使用 Factory DP 创建的。它是使用 create 方法创建的,如果你不覆盖最后一个,它将使用 Doctrine\ORM\EntityManager 中的基类,因此返回一个 Doctrine\ORM\EntityManager 对象。
    • AFAIK,文档中没有任何内容描述如何覆盖实体管理器。你的意思是参数设置在哪里?
    • 不,我的意思是为什么你也必须重写 create 函数。实际上,覆盖public static function create 是很正常的,因为每次创建 EntityManager 时都会使用此方法(构造函数被标记为受保护)。感谢您的帮助,我在教义的代码中找到了这一点,否则我不会进入 Doctrine\ORM\EntityManager 的代码。
    • 您知道如何将@session 服务添加到我的自定义EntityManager 中吗?
    【解决方案3】:

    至少在 Doctrine/ORM 2.4 中,EntityManager 类的文档字符串明确不鼓励从 Doctrine\ORM\EntityManager 继承,而是建议从 Doctrine\ORM\Decorator\EntityManagerDecorator 继承:

    /**
     * The EntityManager is the central access point to ORM functionality.
     * ...
     * You should never attempt to inherit from the EntityManager: Inheritance
     * is not a valid extension point for the EntityManager. Instead you
     * should take a look at the {@see \Doctrine\ORM\Decorator\EntityManagerDecorator}
     * and wrap your entity manager in a decorator.
     * ...
     */
    /* final */class EntityManager implements EntityManagerInterface
    {
        ...
    

    因此,扩展 EntityManagerDecorator 并进行所需的任何更改。您将需要实现 create() 工厂方法,但您现在不需要复制 EntityManager 的实现:

    use Doctrine\ORM\Decorator\EntityManagerDecorator;
    use Doctrine\Common\EventManager;
    use Doctrine\ORM\Configuration;
    
    class MyEntityManager extends EntityManagerDecorator
    {
        /**
         * {@inheritDoc}
         */
        public function persist($entity)
        {
            // do something interesting
            parent::persist($entity);
        }
    
        public function create($conn, Configuration $config, EventManager $eventManager = null)
        {
            return new self(\Doctrine\ORM\EntityManager::create($conn, $config, $eventManager));
        }
    }
    

    然后覆盖doctrine.orm.entity_manager.class 参数以指向您的自定义实体管理器类。

    文档并未涵盖所有内容,在许多情况下您只需阅读代码即可。

    【讨论】:

    • 请注意,虽然这是在教义中建议的方法,但 symfony 的捆绑包尚未更新以支持 EntityManagerInterface - 请参阅 279
    【解决方案4】:

    我发现扩展实体管理器的过程非常违反直觉, 尽管对依赖注入、服务定位器、代码生成、缓存和装饰器模式等概念有很好的掌握。

    希望这个简洁的示例能为您描绘出一幅清晰的画面(这扩展了@user2563451 的答案)

    Symfony 版本(2018 年 8 月 20 日星期一 13:05:58 CEST)

    $ composer info | grep -E -e symfony/framework  -e 'doctrine/(common|orm|dbal)'
    doctrine/common             v2.9.0             Common Library for Doctrine projects
    doctrine/dbal               v2.8.0             Database Abstraction Layer
    doctrine/orm                v2.6.2             Object-Relational-Mapper for PHP
    symfony/framework-bundle    v4.1.3             Symfony FrameworkBundle
    

    config/services.yaml

    App\Doctrine\ORM\CustomEntityManager:
        public: false # optional afaik
        decorates: doctrine.orm.original_entity_manager
        arguments: [ '@App\Doctrine\ORM\CustomEntityManager.inner' ]
    

    config/packages/doctrine.yaml

    doctrine:
        orm:
            auto_generate_proxy_classes: '%kernel.debug%'
            default_entity_manager: original
            entity_managers:
                original:
                    connection: from_env
                    naming_strategy: doctrine.orm.naming_strategy.underscore
                    auto_mapping: false
                    mappings:
                        TimeTracking:
                            is_bundle: false
                            type: annotation
                            dir: '%kernel.project_dir%/src/php/Model'
                            prefix: TimeTracking\Model
                            alias: TimeTracking
                            mapping: true
                #mapper_number_5:
                #    (...)
    

    src/php/App/Doctrine/ORM/CustomEntityManager.php

    <?php
    
    namespace App\Doctrine\ORM;
    
    use App\Doctrine\ORM\Proxy\SoggyProxyFactory;
    use Doctrine\ORM\Decorator\EntityManagerDecorator;
    use Doctrine\ORM\Proxy\ProxyFactory;
    
    /**
     * Writes custom proxy-class methods with support for the set-or-get-trait
     * @property  ProxyFactory soggyProxyFactory
     */
    class CustomEntityManager extends EntityManagerDecorator
    {
        /// SUPER: __construct(EntityManagerInterface $wrapped) { $this->wrapped = $wrapped; }
        private $soggyProxyFactory;
        public function getProxyFactory() {
            $config = $this->getConfiguration();
            if (null === $this->soggyProxyFactory) {
                $this->soggyProxyFactory = new SoggyProxyFactory(
                    $this,
                    $config->getProxyDir(),
                    $config->getProxyNamespace(),
                    $config->getAutoGenerateProxyClasses()
                );
            }
            return $this->soggyProxyFactory;
        }
    }
    

    参考

    http://symfony.com/doc/current/service_container/service_decoration.html

    https://symfony.com/doc/current/doctrine/multiple_entity_managers.html

    【讨论】:

      猜你喜欢
      • 2013-06-13
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2023-01-19
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多