2014-01-20 32 views
16

デフォルトでは、Doctrineでの自己参照ManyToManyの関係には、documentationで説明されているように、所有側と逆側が関係します。Doctrineの多対多自己参照と相反

両側の相違なく、相反的な関連付けを実装する方法はありますか?ドキュメントの例に続き

:だから

<?php 
/** @Entity **/ 
class User 
{ 
    // ... 

    /** 
    * @ManyToMany(targetEntity="User") 
    **/ 
    private $friends; 

    public function __construct() { 
     $this->friends = new \Doctrine\Common\Collections\ArrayCollection(); 
    } 

    // ... 
} 

、追加はentity1entity2にS friendsentity2entity1の友達になることを意味します。

答えて

41

「友人」の関係の要件に応じて、この問題を解決するにはいくつかの方法があります。

単方向

単純なアプローチは、一方向の多対多の関連付けを使用することで、それがどこ双方向1(同期の両側を保つ)かのように扱うでしょう:

/** 
* @Entity 
*/ 
class User 
{ 
    /** 
    * @Id 
    * @Column(type="integer") 
    */ 
    private $id; 

    /** 
    * @ManyToMany(targetEntity="User") 
    * @JoinTable(name="friends", 
    *  joinColumns={@JoinColumn(name="user_a_id", referencedColumnName="id")}, 
    *  inverseJoinColumns={@JoinColumn(name="user_b_id", referencedColumnName="id")} 
    *) 
    * @var \Doctrine\Common\Collections\ArrayCollection 
    */ 
    private $friends; 

    /** 
    * Constructor. 
    */ 
    public function __construct() 
    { 
     $this->friends = new \Doctrine\Common\Collections\ArrayCollection(); 
    } 

    /** 
    * @return array 
    */ 
    public function getFriends() 
    { 
     return $this->friends->toArray(); 
    } 

    /** 
    * @param User $user 
    * @return void 
    */ 
    public function addFriend(User $user) 
    { 
     if (!$this->friends->contains($user)) { 
      $this->friends->add($user); 
      $user->addFriend($this); 
     } 
    } 

    /** 
    * @param User $user 
    * @return void 
    */ 
    public function removeFriend(User $user) 
    { 
     if ($this->friends->contains($user)) { 
      $this->friends->removeElement($user); 
      $user->removeFriend($this); 
     } 
    } 

    // ... 

} 

ます$userA->addFriend($userB)$userBとお友達のコレクションには$userA$userA$userBのフレンズコレクションに追加されます。

また、「友人」テーブル(1,2および2,1)に2つのレコードが追加されます。これは重複したデータと見なすことができますが、コードを大幅に簡略化します。たとえば、あなたが$userAのすべての友人を見つける必要があるとき、あなたは、単に行うことができます。

SELECT u FROM User u JOIN u.friends f WHERE f.id = :userId 

あなたが双方向関連と同じように2つの異なるプロパティをチェックする必要はありません。双方向の関連付けを使用する場合

双方向

Userエンティティは、例えば2つの特性、$myFriends$friendsWithMeを有するであろう。上記と同じ方法でそれらを同期させることができます。

主な違いは、データベースレベルでは、関係(1,2または2,1)を表すレコードが1つしかないことです。これにより、両方のプロパティをチェックする必要があるため、「すべての友だちを探す」クエリが少し複雑になります。

もちろん、addFriend()$myFriends$friendsWithMeの両方を更新して(そして反対側を同期させて)確実にすることで、データベースに2つのレコードを使用することができます。エンティティの複雑さが増しますが、クエリは少し複雑になります。

OneToMany/ManyToOne

ユーザーが友人を追加できるシステムが必要ですが、その友人は、彼らが実際に友達であることを確認する必要がある場合、あなたはjoin-はでその確認を保存する必要があります表。ManyToManyの関連付けはなくなりましたが、User < - OneToMany - >Friendship < - ManyToOne - >Userのようなものはもうありません。

あなたはこのテーマに関する私のブログの記事を読むことができます:

+0

単方向は=>プロパティ 'のためのアクセスの種類を判別できませんでした...' – Trix

+0

あなたがする必要があります'id'プロパティをマップします。私は例にそれを追加しました(整数としてマップされていますが、あなたが好きかもしれません)。 –

+0

非常に便利な答えです。私にとって、追加する重要な部分は '$ user-> addFriend($ this);'でした。 – StockBreak

関連する問題