デフォルトでは、Doctrineの下での自己参照ManyToMany
関係には、 ドキュメント で説明されているように、所有側と逆側が含まれます。
双方の違いなしに相互関連を実装する方法はありますか?
ドキュメントの例に従ってください:
<?php
/** @Entity **/
class User
{
// ...
/**
* @ManyToMany(targetEntity="User")
**/
private $friends;
public function __construct() {
$this->friends = new \Doctrine\Common\Collections\ArrayCollection();
}
// ...
}
したがって、entity1
をentity2
s friends
に追加すると、entity2
はentity1
sの友達になります。
この問題を解決する方法はいくつかありますが、すべて「友達」関係の要件によって異なります。
単方向
簡単なアプローチは、単方向のManyToManyアソシエーションを使用し、それを双方向のアソシエーションのように扱うことです(両側の同期を維持します)。
/**
* @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
のfriends-collectionに追加され、$userA
が$userB
のfriends-collectionに追加されます。
また、「友達」テーブルに2つのレコード(1、2、2、1)が追加されます。これは重複データと見なすことができますが、コードが大幅に簡素化されます。たとえば、$userA
のすべての友達を見つける必要がある場合は、次のようにするだけです。
SELECT u FROM User u JOIN u.friends f WHERE f.id = :userId
双方向アソシエーションの場合のように、2つの異なるプロパティをチェックする必要はありません。
双方向
双方向の関連付けを使用する場合、User
エンティティには2つのプロパティがあります。たとえば$myFriends
と$friendsWithMe
です。上記と同じ方法で同期を保つことができます。
主な違いは、データベースレベルでは、関係を表すレコードが1つしかないことです(1,2または2,1のいずれか)。これにより、両方のプロパティを確認する必要があるため、「すべての友達を検索」クエリが少し複雑になります。
もちろん、addFriend()
が$myFriends
と$friendsWithMe
の両方を更新する(そして反対側の同期を維持する)ことを確認することで、データベース内の2つのレコードを使用することもできます。これにより、エンティティがいくらか複雑になりますが、クエリは少し複雑ではなくなります。
OneToMany/ManyToOne
ユーザーが友達を追加できるシステムが必要であるが、その友達が本当に友達であることを確認する必要がある場合は、その確認を結合テーブルに保存する必要があります。これで、ManyToManyの関連付けはなくなりますが、User
<-OneToMany-> Friendship
<-ManyToOne-> User
のようになります。
このテーマに関する私のブログ投稿を読むことができます: