Domanda

Sto lavorando su un complesso progetto symfony che fonde gli oggetti Dottrina ORM con documenti di dottrina PHPCR-ODM. Tutto funziona bene, ma non sono stato in grado di risolvere problemi di iniezione di dipendenza circolare tra gli ascoltatori nel contenitore.

Lo scenario è, ho più documenti ODM che impostano i riferimenti ORM come vengono caricati, che è realizzato tramite un listener di eventi. Una configurazione di esempio è:

services.yml:

example.event_listener.my_document:
    class: Example\Common\EventListener\MyDocumentEventListener
    arguments: [@doctrine]
    tags:
        - { name: doctrine_phpcr.event_listener, event: postLoad }
        - { name: doctrine_phpcr.event_listener, event: prePersist }
.

Example\Common\EventListener\MyDocumentEventListener.php:

namespace Example\Common\EventListener;

use Example\Common\ODM\Document\MyDocument;
use Doctrine\Common\Persistence\Event\LifecycleEventArgs;
use Doctrine\Common\Persistence\ManagerRegistry;
use Doctrine\ODM\PHPCR\DocumentManager;

/**
 * Listener for {@link Example\Common\ODM\Document\MyDocument} events.
 */
class MyDocumentEventListener
{
  /*
   * @var Doctrine\Common\Persistence\ManagerRegistry
   */
  private $managerRegistry;

  /**
   * Constructor.
   *
   * @param Doctrine\Common\Persistence\ManagerRegistry $documentManager A Doctrine {@link Doctrine\Common\Persistence\ManagerRegistry}.
   */
  public function __construct(ManagerRegistry $managerRegistry)
  {
    $this->managerRegistry = $managerRegistry;
  }

  /**
   * After loading a document, ensure that the references exist
   * to each ORM dependency.
   *
   * @param Doctrine\Common\Persistence\Event\LifecycleEventArgs $args
   */
  public function postLoad(LifecycleEventArgs $args)
  {
    if (get_class($args->getObject()) == 'Example\Common\ODM\Document\MyDocument') {
      $this->loadDependencies($args->getObject(), $args->getObjectManager());
    }
  }

  /**
   * Prior to persisting a document, ensure that the references exist
   * to each ORM dependency.
   *
   * @param Doctrine\Common\Persistence\Event\LifecycleEventArgs $args
   */
  public function prePersist(LifecycleEventArgs $args)
  {
    if (get_class($args->getObject()) == 'Example\Common\ODM\Document\MyDocument') {
      $this->loadDependencies($args->getObject(), $args->getObjectManager());
    }
  }

  /**
   * Pull relational information from the ORM database to populate
   * those fields in the {@link Example\Common\ODM\Document\MyDocument} document that
   * require it. Each field is populated as a reference, so it will be
   * loaded from the database only if necessary.
   *
   * @param Example\Common\ODM\Document\MyDocument $document The MyDocument to load dependencies for.
   * @param Doctrine\ODM\PHPCR\DocumentManager $documentManager The DocumentManager for the MyDocument.
   */
  private function loadDependencies(MyDocument $document, DocumentManager $documentManager)
  {
    $reflectionClass = $documentManager->getClassMetadata(get_class($document))->getReflectionClass();

    $exampleProperty = $reflectionClass->getProperty('example');

    $exampleProperty->setAccessible(true);
    $exampleProperty->setValue(
      $document,
      $this->managerRegistry->getManager()->getReference('Example\Common\ORM\Entity\MyEntity', $document->getExampleId())
    );
  }
}
.

Tutto sopra funziona perfettamente bene quando si lavora con oggetti MyDocument. (Questa è fondamentalmente un'implementazione esatta di Cosa è descritto nella documentazione della Dottrina per la miscelazione o ODM MONGODB).

Ora il problema è quando voglio anche fare la retromarcia all'interno della stessa applicazione, vale a dire, voglio anche avere un'entità orm che ha un ascoltatore che riempia un riferimento o riferimenti ai documenti ODM. < / P >.

Senza aggiungere più codice, diciamo che prolunga la mia configurazione services.yml a:

example.event_listener.my_document:
    class: Example\Common\EventListener\MyDocumentEventListener
    arguments: [@doctrine]
    tags:
        - { name: doctrine_phpcr.event_listener, event: postLoad }
        - { name: doctrine_phpcr.event_listener, event: prePersist }

example.event_listener.my_entity:
    class: Example\Common\EventListener\MyEntityEventListener
    arguments: [@doctrine_phpcr]
    tags:
        - { name: doctrine.event_listener, event: prePersist }
        - { name: doctrine.event_listener, event: postLoad }
.

Questo ora fallisce, perché abbiamo una dipendenza circolare: il contenitore cercherà di iniettare il listener ODM negli ascoltatori di DocumentManager, che a sua volta tenta di iniettare il EntityManager, che a sua volta cerca di iniettare i propri ascoltatori, che ciascuno prova per iniettare il DocumentManager, e così via. (Si noti che questo esempio utilizza il Registry anziché il gestore, ma il risultato è lo stesso).

Ho provato alcuni approcci diversi per risolverlo, ma non ho ancora colpito uno che funziona ancora. Qualcuno è stato in grado di ottenere ascoltatori bidirezionali tra Orm e ODM per funzionare in questo modo in un unico progetto?

Ho trovato alcuni esempi attorno a questo, purtroppo. La mia soluzione alternativa dovrebbe finora essere quella di creare un servizio per gestire il carico / persistente di questi oggetti e quindi eseguire tutto attraverso ciò, ma sembra molto hackash rispetto all'utilizzo di un elegante sistema guidato dagli eventi.

È stato utile?

Soluzione

Pulizia di questa vecchia domanda con ciò che si è rivelato essere la risposta corretta come indicato in un commento sopra: iniettando il contenitore completo.Normalmente evito questo, ma si è rivelato essere l'unico modo per risolvere questo particolare problema.

Nel caso in cui qualcuno stia cercando un esempio di un ascoltatore ODM che carica dipendenze basate su ODM, ecco un esempio di funzionamento di ciò che sono arrivato a:

Definizione del servizio:

example.event_listener.odm.my_document:
    class: Example\Common\EventListener\MyDocumentEventListener
    arguments: [@service_container]
    tags:
        - { name: doctrine_phpcr.event_listener, event: postLoad }
.

L'ascoltatore:

<?php

namespace Example\Common\EventListener;

use Example\Common\Document\MyDocument;
use Doctrine\Common\Persistence\Event\LifecycleEventArgs;
use Doctrine\ODM\PHPCR\DocumentManager;
use Symfony\Component\DependencyInjection\Container;

class MyDocumentEventListener
{
  /*
   * @var \Symfony\Component\DependencyInjection\Container
   */
  private $container;

  /**
   * Constructor.
   *
   * @param \Symfony\Component\DependencyInjection\Container $container A Symfony dependency injection container.
   */
  public function __construct(Container $container)
  {
    $this->container = $container;
  }

  /**
   * After loading a document, ensure that the references exist
   * to each ORM dependency.
   *
   * @param \Doctrine\Common\Persistence\Event\LifecycleEventArgs $args
   */
  public function postLoad(LifecycleEventArgs $args)
  {
    if (get_class($args->getObject()) == 'Example\Common\Document\MyDocument') {
      $this->loadDependencies($args->getObject(), $args->getObjectManager());
    }
  }

  /**
   * Pull relational information from the ORM database to populate
   * those fields in the document that require it. Each field is 
   * populated as a reference, so it will be loaded from the database only 
   * if necessary.
   *
   * @param \Example\Common\Document\MyDocument $document The document to load dependencies for.
   * @param \Doctrine\ODM\PHPCR\DocumentManager $documentManager The DocumentManager for the document.
   */
  private function loadDependencies(MyDocument $document, DocumentManager $documentManager)
  {
    $documentReflectionClass = $documentManager->getClassMetadata(get_class($document))->getReflectionClass();

    $someOrmProperty = $documentReflectionClass->getProperty('orm_property');

    $someOrmProperty->setAccessible(true);
    $someOrmProperty->setValue(
      $document,
      $this->container->get('doctrine.orm.entity_manager')->getReference('Example\Common\Model\MyModel', $document->getOrmPropertyId())
    );
  }
}
.

Ciò consente alla classe di documenti di memorizzare un ID a un'entità del modello Orm, e ogni volta che viene caricato il documento, si riempie nel riferimento effettivo al modello ORM all'interno del documento.Questo lo fa comportare come se sapesse della proprietà Orm tutti avanti e funziona abbastanza bene.

Questo esempio utilizza PHPCR-ODM ma funzionerebbe bene anche per il MongoDB ODM.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top