2014-10-08 123 views
1

我正在嘗試創建Symfony Bundle,其中定義的實體可用於一對多/多對一的關係,而無需手動重寫映射。動態映射manyToOne關係

我這樣做是通過訂閱loadClassMetadata事件並添加基於它們實現的接口的映射。這並不像使用ResolveTargetEntityListener那麼簡單,因爲它只是簡單地將接口替換爲具體的類。

一個例子。我有一個地址和一個客戶實體。客戶有很多地址。 但是另一個包可能會重新定義客戶(或者可能有多個地址的完全不同的實體)。爲此,客戶實施了AddressableInterface。爲了便於使用,我在特性中實現了這個接口。

在用戶中,我檢查該類是否實現了AddressableInterface。如果是這樣,則將OneToMany添加到Address,並將ManyToOne添加到實現AddressableInterface的類。 (在這個例子中Customer類)

然而這留下了以下錯誤:

The association Entity\Customer#addresses refers to the owning side field Entity\Address#subject which does not exist.

但我設置到協會在我的用戶兩種方式。

下面是我的代碼的本質。

namespace Entity; 
class Address 
{ 
    public $subject; 
} 

namespace Entity; 
class Customer implements AddressableInterface 
{ 
    use Traits/Addressable; 
} 

namespace Traits; 
trait Addressable //Implements all methods from AddressableInterface 
{ 
    protected $addresses; 

    public function getAddresses() 
    { 
     return $this->addresses; 
    } 

    public function addAddress(AddressInterface $address) 
    { 
     $this->addresses->add($address); 
    } 

    public function removeAddress(AddressInterface $address) 
    { 
     $this->addresses->removeElement($address); 
    } 
} 

而事件訂閱

class DynamicAddressBindingSubscriber implements EventSubscriber 
{ 
    public function getSubscribedEvents() 
    { 
     return [Events::loadClassMetadata]; 
    } 

    public function loadClassMetadata(LoadClassMetadataEventArgs $eventArgs) 
    { 
     $metadata = $eventArgs->getClassMetadata(); 

     $class = $metadata->getReflectionClass(); 
     if (!in_array(AddressableInterface::class, $class->getInterfaceNames())) { 
      return; 
     } 

     $factory = new \Doctrine\ORM\Mapping\ClassMetadataFactory; 
     $factory->setEntityManager($eventArgs->getEntityManager()); 
     $addressMetadata = $factory->getMetadataFor(Address::class); 

     $addressMetadata->mapManyToOne(
      [ 
       "targetEntity" => $class->getName(), 
       "fieldName" => "subject", 
       "inversedBy" => "addresses" 
      ] 
     ); 

     $metadata->mapOneToMany(
      [ 
       'targetEntity' => Address::class, 
       'fieldName' => 'addresses', 
       'mappedBy'  => 'subject' 
      ] 
     ); 
    } 
} 

我已經看了多個例子並根據我的大部分上this article代碼和教條捆綁源。但是我堅持這一點,因爲我不知道協會爲什麼找不到欠款方。

+1

尼斯的問題,我只是想知道,如果你真的需要反向關係。否則,您不需要增加「地址」的元數據。此外,如果多個實體在同一應用程序中具有地址(例如公司,人員),則它們將避免衝突,它們不能由「主體」映射)。 – fejese 2014-10-08 09:15:41

+0

確實看起來不像我需要反向關係,當我刪除「Address」的元數據時,錯誤仍然保持不變。 – Waaghals 2014-10-08 09:28:44

+0

您是否已從'mapOneToMany'調用中移除'mappedBy'選項? – fejese 2014-10-08 09:31:46

回答

2

您的地址類不具有主題字段的getter/setter。

另一件事是,如果你想綁定地址到任何類,你可能更喜歡使它成爲manyToMany關係。我喜歡這個附件,這樣做:

$metadata->mapManyToMany([ 
    'targetEntity' => '...\FilesBundle\Entity\Attachment', 
    'fieldName' => 'attachments', 
    'cascade' => array('persist'), 
    'joinTable' => array(
     'name' => strtolower($namingStrategy->classToTableName($metadata->getName())) . '_attachment', 
     'joinColumns' => array(
      array(
       'name' => $namingStrategy->joinKeyColumnName($metadata->getName()), 
       'referencedColumnName' => $namingStrategy->referenceColumnName(), 
       'onDelete' => 'CASCADE', 
       'onUpdate' => 'CASCADE', 
      ), 
     ), 
     'inverseJoinColumns' => array(
      array(
       'name' => 'file_id', 
       'referencedColumnName' => $namingStrategy->referenceColumnName(), 
       'onDelete' => 'CASCADE', 
       'onUpdate' => 'CASCADE', 
      ), 
     ) 
    ) 
]); 

其中namingStrategy來自事件:

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

$主題實際上是保護與getter和setter,但爲了簡潔,我留下了它們。 ManyToMany的任何具體原因,在我提到的文章中他們還定義了ManyToMany而不是OneToMany? – Waaghals 2014-10-08 12:22:30

+1

如果允許標記與接口的關係,則允許地址與任何類關聯。雖然您可以在給定類的LoadClassMetadata上動態引入OneToMany,但不能在數據庫中動態更改OneToMany,這意味着您不能說「此時的地址與客戶有關,所以'主題'是指客戶並且在另一個執行更改它「 你只能通過M2M實現它,併爲每個關係地址創建一個連接表<> another_class – 2014-10-08 12:26:22