Pregunta

Estoy trabajando en un complejo proyecto de Symfony que combina a la Doctrina ORM objetos con la Doctrina PHPCR-ODM documentos.Todo funciona bien, pero he sido incapaz de resolver circular de inyección de dependencia de problemas entre los oyentes en el contenedor.

El escenario es, tengo varias ODM documentos que establecen ORM referencias a medida que se cargan, que se logra a través de un detector de eventos.Un ejemplo de la configuración es:

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

Todo lo anterior funciona perfectamente bien cuando se trabaja con MyDocument objetos.(Esto es básicamente una exacta aplicación de lo que se describe en la Doctrina de la documentación para la mezcla ORM y MongoDB ODM).

Ahora, el problema es cuando yo también quiero hacer la inversa dentro de la misma aplicación, es decir, yo también quiero tener un ORM entidad que tiene un oyente que se llena en una referencia o referencias a los ODM documentos.

Sin añadir más código, digamos que yo extienda mi services.yml configuración:

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 }

Esto ahora va a fallar, porque tenemos una dependencia circular:el contenedor intenta inyectar el ODM oyente en la DocumentManager's oyentes, que a su vez intenta inyectar el EntityManager, que a su vez intenta inyectar sus propios oyentes, que cada uno intenta inyectar el DocumentManager, y así sucesivamente.(Observe que en este ejemplo se utiliza la Registry en lugar de la gerente, pero el resultado es el mismo).

He intentado un par de diferentes enfoques para resolver esto, pero no he golpeado en uno que funciona todavía.Nadie ha sido capaz de obtener bi-direccional entre los oyentes ORM y ODM para el trabajo de este tipo en un solo proyecto?

He encontrado algunos ejemplos de todo esto, por desgracia.Mi solución hasta el momento sería la de crear un servicio para manejar la carga/la persistencia de estos objetos y, a continuación, ejecute a través de todo lo que se, pero me parece muy hackish comparación con el uso de un elegante evento impulsado por el sistema.

¿Fue útil?

Solución

La limpieza de esta vieja pregunta con lo que resultó ser la respuesta correcta, como se señaló en un comentario anterior:inyectar el contenedor completo.Normalmente puedo evitar esto, pero resultó ser la única manera de resolver este problema en particular.

En caso de que alguien está buscando un ejemplo de una ODM oyente de que la carga ORM dependencias, aquí es un ejemplo de lo que me llegó:

Definición del servicio:

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

El oyente:

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

Esto permite que la clase de documento para almacenar un ID a un modelo de ORM entidad, y cada vez que se carga el documento, se llena en la referencia real para el modelo de ORM en el documento.Esto hace que se comporte como si se sabía sobre el ORM de la propiedad a lo largo de todos, y funciona bastante bien.

En este ejemplo se utiliza PHPCR ODM, pero funcionaría muy bien para MongoDB ODM así.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top