문제

나는 Doctrine ORM 개체와 Doctrine PHPCR-ODM 문서를 혼합하는 복잡한 Symfony 프로젝트를 진행하고 있습니다.모든 것이 잘 작동하지만 컨테이너의 리스너 간의 순환 종속성 주입 문제를 해결할 수 없습니다.

시나리오는 로드될 때 ORM 참조를 설정하는 여러 ODM 문서가 있으며 이는 이벤트 리스너를 통해 수행됩니다.구성 예는 다음과 같습니다.

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

위의 모든 것은 작업할 때 완벽하게 작동합니다. MyDocument 사물.(이것은 기본적으로 교리 문서에 설명된 내용 ORM과 MongoDB ODM을 혼합하는 경우).

이제 문제는 동일한 애플리케이션 내에서 그 반대도 수행하고 싶을 때입니다. 즉, ODM 문서에 대한 참조를 채우는 리스너가 있는 ORM 엔터티도 갖고 싶습니다.

더 많은 코드를 추가하지 않고 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.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 }

이제 순환 종속성이 있으므로 이 작업은 실패합니다.컨테이너는 ODM 리스너를 DocumentManager의 청취자는 차례로 주입을 시도합니다. EntityManager, 이는 차례로 자체 청취자를 주입하려고 시도하며, 각각은 DocumentManager, 등등.(이 예제에서는 Registry 관리자가 아니라 결과는 동일합니다).

이 문제를 해결하기 위해 몇 가지 다른 접근 방식을 시도했지만 아직 작동하는 접근 방식을 찾지 못했습니다.단일 프로젝트에서 ORM과 ODM 사이의 양방향 리스너가 이와 같이 작동하도록 할 수 있었던 사람이 있습니까?

불행히도 이와 관련된 예는 거의 발견되지 않았습니다.지금까지 내 해결 방법은 이러한 개체의 로드/지속을 처리하는 서비스를 만든 다음 이를 통해 모든 것을 실행하는 것이었지만 우아한 이벤트 기반 시스템을 사용하는 것에 비해 매우 해킹적인 것처럼 보입니다.

도움이 되었습니까?

해결책

위의 설명에 언급된 대로 정답으로 판명된 것으로 이 오래된 질문을 정리합니다.전체 용기를 주입합니다.일반적으로 나는 이것을 피하지만 이것이 이 특정 문제를 해결하는 유일한 방법임이 밝혀졌습니다.

ORM 기반 종속성을 로드하는 ODM 리스너의 예를 찾는 사람이 있는 경우 다음은 내가 도달한 작업 예입니다.

서비스 정의:

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

그 듣는 사람:

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

이를 통해 문서 클래스는 ORM 모델 엔터티에 ID를 저장할 수 있으며 문서가 로드될 때마다 문서 내의 ORM 모델에 대한 실제 참조를 채웁니다.이렇게 하면 ORM 속성에 대해 모두 알고 있는 것처럼 동작하고 꽤 잘 작동합니다.

이 예제에서는 PHPCR-ODM을 사용하지만 MongoDB ODM에서도 잘 작동합니다.

라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top