Is there a way to specify Doctrine2 Entitymanager implementation class in Symfony2?

前端 未结 4 669
你的背包
你的背包 2020-11-30 08:18

I\'m currently working with Symfony2 and Doctrine2, but I must override the Doctrine2 EntityManager and add it some \"undelete\" features (ACLs inside).

So I\'m wond

相关标签:
4条回答
  • 2020-11-30 08:44

    Yes, it's possible with two steps:

    1 - Override the doctrine.orm.entity_manager.class parameter to point to your custom entity manager (which should extend Doctrine\ORM\EntityManager.)

    2 - Your custom entity manager must override the create method so that it returns an instance of your class. See my example below, and note the last line regarding 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());
        }
    

    You'll also need to use the following in your class:

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

    To be honest, I'm surprised that the 2nd step is required at all, I would think this should be possible to accomplish using only the service container.

    0 讨论(0)
  • 2020-11-30 08:46

    At least in Doctrine/ORM 2.4, the doc string of the EntityManager class explicitly discourages inheriting from Doctrine\ORM\EntityManager, instead they suggest inheriting from 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
    {
        ...
    

    So, extend EntityManagerDecorator and make whatever changes you need. You will need to implement the create() factory method, but you don't need to copy EntityManager's implementation now:

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

    Then override the doctrine.orm.entity_manager.class parameter to point to your custom entity manager class.

    The docs don't cover everything, in many cases you just have to read the code.

    0 讨论(0)
  • 2020-11-30 08:46

    I found the process of extending the entity manager extremely counterintuitive, despite a decent grasp of concepts including dependency injection, service locator, code generation, caching and the decorator pattern.

    Hopefully this concise example will paint a clear picture for you (this expands on the answer by @user2563451)

    Symfony Version (Mon Aug 20 13:05:58 CEST 2018)

    $ 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;
        }
    }
    

    references

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

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

    0 讨论(0)
  • 2020-11-30 08:50

    After Doctrine 2.4 (Doctrine 2.4 release) you need to use decorator for this. Do not extend EntityManager directly. First you need to implement you own entity manager decorator that extends Doctrine\ORM\Decorator\EntityManagerDecorator (like @Dana) But you can't just change doctrine.orm.entity_manager.class to your new decorator because EntityManagerDecorator requires EntityManagerInterface in it's constructor:

    public function __construct(EntityManagerInterface $wrapped)
    

    You can't just pass doctrine.orm.entity_manager as a parameter here because it will be a recursion. And don't do like this:

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

    What you need is to configure your decorator in services like a decorator:

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

    Now you'll have your decorator as a default entity manager for Doctrine. @yourcompany_entity_manager.inner is actually a link to doctrine.orm.default_entity_manager that will be passed to yourcompany_entity_manager constructor.

    Symfony docs for configuring decorators: link

    Btw this command is very useful to debug your services:

    app/console container:debug | grep entity_manager

    0 讨论(0)
提交回复
热议问题