学说多对多的自我引用和互惠 [英] Doctrine's Many-To-Many Self-Referencing and reciprocity
问题描述
默认情况下,自我引用 ManytoMany
关系在Doctrine下涉及一个拥有方和一个反面,如文档。
By default, self-referencing ManyToMany
relationships under Doctrine involve an owning side and an inverse side, as explained in the documentation.
有没有办法实现双方的互惠关系?
Is there a way to implement a reciprocal association whithout difference between both sides?
按照文档中的示例:
<?php
/** @Entity **/
class User
{
// ...
/**
* @ManyToMany(targetEntity="User")
**/
private $friends;
public function __construct() {
$this->friends = new \Doctrine\Common\Collections\ArrayCollection();
}
// ...
}
所以,将 entity1
添加到 entity2
s 朋友
entity2
将在 entity1
的朋友中。
So, adding entity1
to entity2
s friends
implies that entity2
will be in entity1
s friends.
推荐答案
有很多方法可以解决这个问题,这一切都取决于朋友关系的要求。
There are a number of ways to solve this problem, all depending on what the requirements for the "friends" relation are.
单向
一个简单的方法是使用单向ManyToMany关联,并将其视为双向的关联(保持双方在同步):
A simple approach would be to use a unidirectional ManyToMany association, and treat it as if it where a bidirectional one (keeping both sides in sync):
/**
* @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
。
When you call $userA->addFriend($userB)
, $userB
will be added to the friends-collection in $userA
, and $userA
will be added to the friends-collection in $userB
.
它还会导致2条记录添加到friends表(1 ,2和2,1)。虽然这可以看作是重复的数据,但它会简化你的代码。例如,当您需要查找 $ userA
的所有朋友时,您可以简单地:
It will also result in 2 records added to the "friends" table (1,2 and 2,1). While this can be seen as duplicate data, it will simplify your code a lot. For example when you need to find all friends of $userA
, you can simply do:
SELECT u FROM User u JOIN u.friends f WHERE f.id = :userId
不需要检查2个不同的属性,就像双向关联一样。
No need to check 2 different properties as you would with a bidirectional association.
双向
当使用双向关联时, User
entity将具有2个属性, $ myFriends
和 $ friendsWithMe
例如。您可以按照上述方式使其保持同步。
When using a bidirectional association the User
entity will have 2 properties, $myFriends
and $friendsWithMe
for example. You can keep them in sync the same way as described above.
主要区别在于,在数据库级别上,只有一个记录代表关系(1 ,2或2,1)。这使得查找所有朋友的查询更复杂一些,因为您必须检查这两个属性。
The main difference is that on a database level you'll only have one record representing the relationship (either 1,2 or 2,1). This makes "find all friends" queries a bit more complex because you'll have to check both properties.
您当然可以在数据库中使用2条记录确定 addFriend()
将更新 $ myFriends
和 $ friendsWithMe
(并保持对方同步)。这将增加您的实体的一些复杂性,但查询变得不那么复杂。
You could of course still use 2 records in the database by making sure addFriend()
will update both $myFriends
and $friendsWithMe
(and keep the other side in sync). This will add some complexity in your entities, but queries become a little less complex.
OneToMany / ManyToOne
如果您需要一个用户可以添加朋友的系统,但该朋友必须确认他们确实是朋友,则需要将该确认存储在连接表中。然后你不再有一个ManyToMany关联,但是像用户
< - OneToMany - > 友谊
< - ManyToOne - > 用户
。
If you need a system where a user can add a friend, but that friend has to confirm that they are indeed friends, you'll need to store that confirmation in the join-table. You then no longer have a ManyToMany association, but something like User
<- OneToMany -> Friendship
<- ManyToOne -> User
.
您可以阅读我关于这个主题的博文:
You can read my blog-posts on this subject:
- Doctrine 2: How to handle join tables with extra columns
- More on one-to-many/many-to-one associations in Doctrine 2
这篇关于学说多对多的自我引用和互惠的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!