Question

Je travaille sur un projet Symfony complexe qui mélange des objets Doctrine ORM avec des documents Doctrine PHPCR-ODM.Tout fonctionne bien, mais je n'ai pas pu résoudre les problèmes d'injection de dépendances circulaires entre les auditeurs du conteneur.

Le scénario est le suivant : j'ai plusieurs documents ODM qui définissent des références ORM au fur et à mesure de leur chargement, ce qui est réalisé via un écouteur d'événements.Un exemple de configuration est :

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

Tout ce qui précède fonctionne parfaitement lorsque vous travaillez avec MyDocument objets.(Il s'agit essentiellement d'une implémentation exacte de ce qui est décrit dans la documentation Doctrine pour mélanger ORM et MongoDB ODM).

Maintenant, le problème est que je veux aussi faire l'inverse dans la même application - c'est-à-dire que je veux aussi avoir une entité ORM qui a un écouteur qui remplit une ou plusieurs références à des documents ODM.

Sans ajouter plus de code, disons que j'étends mon services.yml configuration pour :

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 }

Cela va maintenant échouer, car nous avons une dépendance circulaire :le conteneur essaie d'injecter l'écouteur ODM dans le DocumentManager, qui à son tour tente d'injecter le EntityManager, qui à son tour tente d'injecter ses propres auditeurs, qui tentent chacun d'injecter le DocumentManager, et ainsi de suite.(Notez que cet exemple utilise le Registry plutôt que le manager, mais le résultat est le même).

J'ai essayé plusieurs approches différentes pour résoudre ce problème, mais je n'en ai pas encore trouvé une qui fonctionne.Quelqu'un a-t-il réussi à faire fonctionner des auditeurs bidirectionnels entre ORM et ODM de cette manière dans un seul projet ?

J'ai malheureusement trouvé peu d'exemples à ce sujet.Jusqu'à présent, ma solution de contournement serait de créer un service pour gérer le chargement/la persistance de ces objets, puis de tout exécuter à travers cela, mais cela semble très hackish par rapport à l'utilisation d'un élégant système piloté par les événements.

Était-ce utile?

La solution

Nettoyer cette vieille question avec ce qui s'est avéré être la bonne réponse, comme indiqué dans un commentaire ci-dessus :injecter le récipient plein.Normalement, j'évite cela, mais cela s'est avéré être le seul moyen de résoudre ce problème particulier.

Si quelqu'un recherche un exemple d'écouteur ODM qui charge des dépendances basées sur ORM, voici un exemple concret de ce à quoi je suis arrivé :

Définition du service :

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

L'auditeur:

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

Cela permet à la classe de document de stocker un identifiant pour une entité de modèle ORM, et chaque fois que le document est chargé, elle remplit la référence réelle au modèle ORM dans le document.Cela le fait se comporter comme s'il connaissait la propriété ORM depuis le début et fonctionne plutôt bien.

Cet exemple utilise PHPCR-ODM mais fonctionnerait également très bien pour MongoDB ODM.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top