【问题标题】:Doctrine's Many-To-Many Self-Referencing and reciprocityDoctrine 的多对多自引用和互惠
【发布时间】:2014-01-20 21:47:11
【问题描述】:

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

有没有办法实现互惠关联,双方之间没有区别?

按照文档中的示例:

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

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

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

    // ...
}

因此,将entity1 添加到entity2s friends 意味着entity2 将在entity1s 朋友中。

【问题讨论】:

    标签: php doctrine-orm doctrine many-to-many


    【解决方案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-&gt;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 个不同的属性。

    双向

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

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

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

    OneToMany / ManyToOne

    如果您需要一个用户可以添加朋友的系统,但该朋友必须确认他们确实是朋友,您需要将该确认存储在联接表中。然后,您不再有 ManyToMany 关联,而是 User Friendship User

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

    【讨论】:

    • 单向 => 无法确定属性 ... 的访问类型
    • 您需要映射id 属性。我已将它添加到示例中(映射为整数,但它可以是任何你喜欢的)。
    • 非常有用的答案。对我来说,要添加的重要部分是$user-&gt;addFriend($this);
    • 克隆此实体时,它没有采用双向链接。但我必须添加“ public function __clone() { foreach($this->getFriends() as $thatFriend) { $thatFriend->addFriend($this); } }”
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2011-03-18
    • 1970-01-01
    • 2014-07-05
    • 2014-09-14
    • 1970-01-01
    • 1970-01-01
    • 2014-12-20
    相关资源
    最近更新 更多