Esiste un modo per specificare la class di implementazione di Doctrine2 Entitymanager in Symfony2?

Attualmente sto lavorando con Symfony2 e Doctrine2, ma devo scavalcare Doctrine2 EntityManager e aggiungerlo alcune funzionalità “undelete” (ACL al suo interno).

Quindi mi chiedo: c’è un modo per sovrascrivere la class EntityManager e specificare Doctrine2 in Symfony2 per usarlo come implementazione di EntityManager?

Grazie per tutto l’aiuto!

Sì, è ansible con due passaggi:

1 – Sostituisci il parametro doctrine.orm.entity_manager.class in modo che punti al tuo gestore personalizzato (che dovrebbe estendere Doctrine\ORM\EntityManager .)

2 – Il gestore personalizzato deve sovrascrivere il metodo di create modo che restituisca un’istanza della class. Vedi il mio esempio qui sotto e nota l’ultima riga riguardante 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()); } 

Avrai anche bisogno di use quanto segue nella tua class:

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

Per essere onesti, sono sorpreso che il secondo passaggio sia necessario, penso che ciò dovrebbe essere ansible utilizzando solo il contenitore di servizi.

Dopo Doctrine 2.4 (versione di Doctrine 2.4 ) è necessario utilizzare decorator per questo. Non estendere direttamente EntityManager. Per prima cosa è necessario implementare il proprio decoratore di entity manager che estenda Doctrine \ ORM \ Decorator \ EntityManagerDecorator (come @Dana) ma non si può semplicemente cambiare doctrine.orm.entity_manager.class nel nuovo decoratore perché EntityManagerDecorator richiede EntityManagerInterface nel suo costruttore:

 public function __construct(EntityManagerInterface $wrapped) 

Non puoi semplicemente passare doctrine.orm.entity_manager come parametro qui perché sarà una ricorsione. E non fare così:

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

Quello di cui hai bisogno è configurare il tuo decoratore in servizi come un decoratore:

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

Ora avrai il tuo decoratore come gestore di quadro predefinito per Doctrine. @ yourcompany_entity_manager.inner è in realtà un collegamento a doctrine.orm.default_entity_manager che verrà passato al costruttore yourcompany_entity_manager .

Documenti Symfony per la configurazione dei decoratori: link

Btw questo comando è molto utile per eseguire il debug dei tuoi servizi:

contenitore app / console: debug | grep entity_manager

Almeno in Doctrine / ORM 2.4, la stringa doc della class EntityManager sconsiglia esplicitamente di ereditare da Doctrine \ ORM \ EntityManager, suggerendo invece di ereditare da 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 { ... 

Quindi, estendi EntityManagerDecorator e apporta le modifiche necessarie. È necessario implementare il metodo factory create (), ma non è necessario copiare l’implementazione di EntityManager ora:

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

Quindi eseguire l’override del parametro doctrine.orm.entity_manager.class in modo che punti alla class di gestione entity framework personalizzata.

I documenti non coprono tutto, in molti casi basta leggere il codice.

Ho scoperto che il processo di estensione del gestore dell’ quadro era estremamente controintuitivo, nonostante una buona conoscenza dei concetti, inclusi l’iniezione di dipendenza, l’individuazione dei servizi, la generazione del codice, il caching e il modello di decoratore.

Speriamo che questo esempio conciso dipinga un’immagine chiara per te (questo espande la risposta di @ user2563451)

Versione di Symfony (lunedì 20 agosto 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 / pacchetti / doctrine.yaml

 doctrine: orm: auto_generate_proxy_classs: '%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 / Dottrina / 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; } } 

Riferimenti

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

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