2017-12-21 48 views
3

태그가있는 엔티티가 두 개 있습니다.동적 Doctrine 관계 inverse side methods

class TaggableListener implements EventSubscriber 
{ 
    /** 
    * @return array 
    */ 
    public function getSubscribedEvents() 
    { 
     return [ 
      Events::loadClassMetadata 
     ]; 
    } 

    /** 
    * @param \Doctrine\ORM\Event\LoadClassMetadataEventArgs $eventArgs 
    */ 
    public function loadClassMetadata(LoadClassMetadataEventArgs $eventArgs) 
    { 
     // the $metadata is the whole mapping info for this class 
     $metadata = $eventArgs->getClassMetadata(); 

     $class = $metadata->getReflectionClass(); 
     if (!$class->implementsInterface(TaggableEntityInterface::class)) { 
      return; 
     } 

     $namingStrategy = $eventArgs 
      ->getEntityManager() 
      ->getConfiguration() 
      ->getNamingStrategy() 
     ; 

     $metadata->mapManyToMany([ 
      'targetEntity' => Tag::class, 
      'fieldName'  => 'tags', 
      'cascade'  => ['persist'], 
      'joinTable'  => [ 
       'name'  => $namingStrategy->classToTableName($metadata->getName()) . '__Tags', 
       'joinColumns' => [ 
        [ 
         'name'     => $namingStrategy->joinKeyColumnName($metadata->getName()), 
         'referencedColumnName' => $namingStrategy->referenceColumnName(), 
         'onDelete' => 'CASCADE', 
         'onUpdate' => 'CASCADE', 
        ], 
       ], 
       'inverseJoinColumns' => [ 
        [ 
         'name'     => 'tag_id', 
         'referencedColumnName' => $namingStrategy->referenceColumnName(), 
         'onDelete' => 'CASCADE', 
         'onUpdate' => 'CASCADE', 
        ], 
       ] 
      ] 
     ]); 
    } 
} 

interface TaggableEntityInterface 
{ 
    public function addTag(Tag $tags); 
    public function removeTag(Tag $tags); 
    public function getTags(); 
} 

trait Tags 
{ 
    protected $tags; 

    public function addTag(Tag $tags) { /*..*/ }; 
    public function removeTag(Tag $tags) { /*..*/ }; 
    public function getTags() { /*..*/ }; 
} 

내가 $category->getTags() 또는 $product->getTags()를 사용하여 모든 태그를 얻을 수 있습니다 :이 blog post에 본사를 둔 역동적 인 관계를 만들었습니다. 이것은 잘 작동합니다. 그것은 오래된 블로그 게시물이지만, 분명히 구식이 아닙니다.

그러나 방법이 없으므로 태그와 관련된 모든 제품을 쉽게 얻을 수있는 방법이 없습니다.

이상적으로 TaggableEntityInterface[]을 반환하는 Tag::getRelatedEntities()이 필요하므로 컬렉션에 ProductCategory 엔티티가 모두 포함되어 있어야합니다. 내 템플릿에서

,이 같은 것을 생각하고 :

Tag::getRelatedEntities() 및/또는 Tag::getProducts() 방법을 얻을 수있는 가장 좋은 방법은 무엇입니까
<h1>Related entities:</h1> 
<ul> 
{% for relatedEntity in tag.getRelatedEntities %} 
    <li>{{ relatedEntity }}</li> //..implementing `__toString()` 
{% endfor %} 
</ul> 

?

물론 내 태그 엔티티에 수동으로 getProducts 메소드를 생성 할 수는 있지만 동적 관계라는 생각을 깨뜨릴 수 있습니다. 이 예제에서는 Product 및 Category 엔티티 만 있었지만 실제로는 Taggable 인 엔티티가 많이 있습니다.

답변

2

Doctrine에서 여러 개의 다른 엔터티를 가리키는 단일 관계를 만들 수 없습니다. 즉, TagrelatedEntities이 있으면 동일한 클래스 또는 매핑 된 수퍼 클래스의 객체 배열이어야합니다.

당신이 할 수있는 일은 모든 Doctrine 관리 클래스를 반복하고, 주어진 인터페이스를 구현하고, 태그로 해당 엔티티를 검색하고, 모두 단일 배열로 병합하는 서비스를 작성하는 것입니다.

예제 코드 : 쉽게 템플릿에서이 기능에 액세스하려면

$entities = []; 
foreach ($entityManager->getMetadataFactory()->getAllMetadata() as $classMetadata) { 
    if (in_array(TaggableEntityInterface::class, class_implements($classMetadata->getName()))) { 
     $repository = $entityManager->getRepository($classMetadata->getName()); 
     $entities = array_merge($entities, $repository->findBy(['tag' => $tag])); 
    } 
} 

또한 나뭇 가지 기능으로이 문제를 마무리 할 수있다.

코드는 실제로 테스트되지 않았으므로 사용 케이스에 따라 약간의 수정이나 개선이 필요할 수 있습니다.

+0

'Where 절에'알 수없는 열 'Product__Tags.tag_id'오류 메시지가 있습니다. 어떤 생각? –

+0

이전 (또는 스키마 : 업데이트)을 실행 했습니까? Doctrine은 사용할 열을 알고 있기 때문에 데이터베이스에는 존재하지 않습니다. –