学说多对多的自我引用和互惠 [英] Doctrine's Many-To-Many Self-Referencing and reciprocity

查看:106
本文介绍了学说多对多的自我引用和互惠的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

默认情况下,自我引用 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 entity2s friends implies that entity2 will be in entity1s 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屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆