¿Hay alguna manera de especificar la clase de implementación de Doctrine2 Entitymanager en Symfony2?

Actualmente estoy trabajando con Symfony2 y Doctrine2, pero debo anular Doctrine2 EntityManager y agregar algunas características de “recuperación” (ACL en el interior).

Entonces me pregunto: ¿hay alguna manera de anular la clase EntityManager y especificar Doctrine2 en Symfony2 para usarlo como implementación de EntityManager?

¡Gracias por cualquier ayuda!

Sí, es posible con dos pasos:

1 – Anule el parámetro doctrine.orm.entity_manager.class para que apunte a su administrador de entidades personalizado (que debe extender Doctrine\ORM\EntityManager ).

2 – Su administrador de entidad personalizado debe anular el método de create para que devuelva una instancia de su clase. Vea mi ejemplo a continuación y observe la última línea con respecto a 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()); } 

También necesitarás use lo siguiente en tu clase:

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

Para ser honesto, estoy sorprendido de que se requiera el 2º paso, creo que esto debería ser posible con solo usar el contenedor de servicios.

Después de Doctrine 2.4 ( lanzamiento de Doctrine 2.4 ) necesita usar decorador para esto. No extienda EntityManager directamente. Primero necesita implementar su propio decorador de administrador de entidades que extiende Doctrine \ ORM \ Decorator \ EntityManagerDecorator (como @Dana) Pero no puede simplemente cambiar doctrine.orm.entity_manager.class a su nuevo decorador porque EntityManagerDecorator requiere EntityManagerInterface en su constructor:

 public function __construct(EntityManagerInterface $wrapped) 

No puede simplemente pasar doctrine.orm.entity_manager como parámetro aquí porque será una recursión. Y no hagas así:

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

Lo que necesita es configurar su decorador en servicios como un decorador:

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

Ahora tendrá a su decorador como administrador de entidad predeterminado para Doctrine. @ yourcompany_entity_manager.inner es en realidad un enlace a doctrine.orm.default_entity_manager que se pasará a yourcompany_entity_manager constructor.

Documentos de Symfony para configurar decoradores: enlace

Por cierto, este comando es muy útil para depurar sus servicios:

contenedor de la aplicación / consola: depuración | grep entity_manager

Al menos en Doctrine / ORM 2.4, la cadena de documentación de la clase EntityManager desalienta explícitamente la herencia de Doctrine \ ORM \ EntityManager, en su lugar sugieren heredar de 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 { ... 

Por lo tanto, extienda EntityManagerDecorator y realice los cambios que necesite. Necesitará implementar el método de fábrica create (), pero no necesita copiar la implementación de EntityManager ahora:

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

A continuación, anule el parámetro doctrine.orm.entity_manager.class para que apunte a su clase de administrador de entidades personalizado.

Los documentos no cubren todo, en muchos casos solo tienes que leer el código.

Descubrí que el proceso de extender el administrador de entidades es extremadamente contrario a la intuición, a pesar de una comprensión decente de los conceptos que incluyen la dependency injection, el localizador de servicios, la generación de código, el almacenamiento en caché y el patrón de decorador.

Esperemos que este ejemplo conciso muestre una imagen clara para ti (esto expande la respuesta por @ user2563451)

Versión de Symfony (Lun 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

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

referencias

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

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