教义的多对多自我参照和互惠

2022-08-30 18:49:43

默认情况下,Doctrine下的自引用关系涉及拥有方和反向方,如文档中所述。ManyToMany

有没有办法实现双方之间的差异的互惠联想?

按照文档中的示例进行操作:

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

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

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

    // ...
}

因此,添加到s意味着将在s朋友中。entity1entity2friendsentity2entity1


答案 1

有很多方法可以解决这个问题,这一切都取决于对“朋友”关系的要求。

单向

一个简单的方法是使用单向 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$userA$userB

它还将导致将 2 条记录添加到“朋友”表(1,2 和 2,1)。虽然这可以被视为重复数据,但它将大大简化您的代码。例如,当您需要查找 的所有朋友时,您只需执行以下操作即可:$userA

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

无需像使用双向关联那样检查 2 个不同的属性。

双向

例如,使用双向关联时,实体将具有 2 个属性。您可以按照上述相同方式使它们保持同步。User$myFriends$friendsWithMe

主要区别在于,在数据库级别上,您只有一条表示关系的记录(1,2 或 2,1)。这使得“查找所有朋友”查询变得更加复杂,因为您必须同时检查这两个属性。

当然,您仍然可以在数据库中使用2条记录,方法是确保将更新两者并保持另一端同步)。这将增加实体的复杂性,但查询会变得不那么复杂。addFriend()$myFriends$friendsWithMe

一对多 / 多对一

如果您需要一个用户可以添加好友的系统,但该好友必须确认他们确实是好友,则需要将该确认存储在联接表中。然后,您不再有Many ToMany关联,而是像<- OneToMany -> <- ManyToOne ->之类的东西。UserFriendshipUser

您可以阅读我关于此主题的博客文章:


答案 2

推荐