Frage

Ich arbeite an einem komplexen Symfony-Projekt, das Doctrine ORM-Objekte mit Doctrine PHPCR-ODM-Dokumenten verbindet.Alles funktioniert gut, aber ich konnte die Probleme der zirkulären Abhängigkeitsinjektion zwischen Listenern im Container nicht lösen.

Das Szenario sieht so aus, dass ich über mehrere ODM-Dokumente verfüge, die beim Laden ORM-Referenzen festlegen, was über einen Ereignis-Listener erfolgt.Eine Beispielkonfiguration ist:

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

Alles oben Genannte funktioniert bei der Arbeit einwandfrei MyDocument Objekte.(Dies ist im Grunde eine exakte Implementierung von was in der Doctrine-Dokumentation beschrieben ist zum Mischen von ORM und MongoDB ODM).

Das Problem besteht nun darin, dass ich innerhalb derselben Anwendung auch das Umgekehrte tun möchte – das heißt, ich möchte auch eine ORM-Entität haben, die über einen Listener verfügt, der einen oder mehrere Verweise auf ODM-Dokumente ausfüllt.

Nehmen wir an, ich erweitere meine, ohne weiteren Code hinzuzufügen services.yml Konfiguration für:

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 }

Dies wird nun scheitern, da wir eine zirkuläre Abhängigkeit haben:Der Container versucht, den ODM-Listener in den einzuschleusen DocumentManager's Zuhörer, die wiederum versuchen, das zu injizieren EntityManager, das wiederum versucht, seine eigenen Zuhörer einzuspeisen, die jeweils versuchen, das einzuspeisen DocumentManager, und so weiter.(Beachten Sie, dass in diesem Beispiel das verwendet wird Registry und nicht der Manager, aber das Ergebnis ist das gleiche).

Ich habe verschiedene Lösungsansätze ausprobiert, bin aber noch nicht auf einen gestoßen, der funktioniert.Konnte jemand bidirektionale Listener zwischen ORM und ODM dazu bringen, in einem einzigen Projekt so zu arbeiten?

Leider habe ich dazu nur wenige Beispiele gefunden.Meine bisherige Problemumgehung bestand darin, einen Dienst zu erstellen, der das Laden/Persistentieren dieser Objekte übernimmt, und dann alles darüber laufen zu lassen, aber im Vergleich zur Verwendung eines eleganten ereignisgesteuerten Systems scheint es sehr hackig zu sein.

War es hilfreich?

Lösung

Bereinigen Sie diese alte Frage mit der richtigen Antwort, wie in einem Kommentar oben erwähnt:Einspritzen des vollen Behälters.Normalerweise vermeide ich das, aber es stellte sich heraus, dass es die einzige Möglichkeit war, dieses spezielle Problem zu lösen.

Falls jemand nach einem Beispiel für einen ODM-Listener sucht, der ORM-basierte Abhängigkeiten lädt, hier ist ein funktionierendes Beispiel dafür, was ich herausgefunden habe:

Dienstdefinition:

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

Der Zuhörer:

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

Dadurch kann die Dokumentklasse eine ID für eine ORM-Modellentität speichern und jedes Mal, wenn das Dokument geladen wird, den tatsächlichen Verweis auf das ORM-Modell innerhalb des Dokuments ausfüllen.Dadurch verhält es sich so, als ob es die ORM-Eigenschaft von Anfang an gewusst hätte, und funktioniert ziemlich gut.

Dieses Beispiel verwendet PHPCR-ODM, würde aber auch gut für MongoDB ODM funktionieren.

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top