symfony Doctrine -- Cвязанные сущности -- Пример Many To Many связи (с использованием PersistentCollection)

Здесь описан пример, как создать связь("друзей") в таблице User (Пользователи) -- Many To Many (многие ко многим), и в частности Self-referencing (связь данной таблицы с самой собой). Можно будет в контроллере достать всех пользователей - "друзей" данного пользователя.

Entity User

  • Создаём аннотации для связи Many-To-Many в соответствии с документацией
  • Свойства $friendsWithMe и $myFriends - объекты ArrayCollection. Это определяется в конструкторе класса. О том, кто такой ArrayCollection, для чего он нужен и с чем его едят, можно почитать здесь.
    И для каждого поля методы:
    addProp() - добавляет юзера в коллекцию (в свойство users) и устанавливает связь между "друзьями",
    removeUser() - удаляет юзера из коллекции,
    getUsers() - возвращает список всех "друзей".
  • По данным аннотациям Doctrine создаст вспомогательную таблицу friends, в которой будет 2 поля: id одного и второго "друга".

Так как у нас пример Self-referencing связи, оба связывающих поля и все 6 методов, управляющих ими находятся в одной сущности.

<?php
namespace AppBundle\Entity;

use Doctrine\ORM\Mapping as ORM;
use Doctrine\Common\Collections\ArrayCollection;

/**
 * @ORM\Entity
 * @ORM\Table(name="`user`")
 */
class User
{
    /**
     * @var int
     * 
     * @ORM\Id
     * @ORM\Column(type="integer")
     * @ORM\GeneratedValue(strategy="SEQUENCE")
     * @ORM\SequenceGenerator(sequenceName="user_id_seq", allocationSize=1, initialValue=1)
     */
    protected $id;
      
    /**
     * Many Users have Many Users.
     * 
     * @var ArrayCollection
     * 
     * @ORM\ManyToMany(targetEntity="User", mappedBy="myFriends")
     */
    protected $friendsWithMe;

    /**
     * Many Users have many Users.
     * 
     * @var ArrayCollection
     * 
     * @ORM\ManyToMany(targetEntity="User", inversedBy="friendsWithMe")
     * @ORM\JoinTable(name="friends",
     *      joinColumns={@ORM\JoinColumn(name="user_id", referencedColumnName="id")},
     *      inverseJoinColumns={@ORM\JoinColumn(name="friend_user_id", referencedColumnName="id")}
     *      )
     */
    protected $myFriends;

     public function __construct()
    {
        parent::__construct();

        $this->friendsWithMe = new ArrayCollection();
        $this->myFriends = new ArrayCollection();
    }

    /**
     * @return ArrayCollection
     */
    public function getFriendsWithMe()
    {
        return $this->friendsWithMe;
    }

    /**
     * @param User $myFriend
     *
     * @return User
     */
    public function addMyFriend(User $myFriend)
    {
        if (!$this->myFriends->contains($myFriend)) {
            $this->myFriends[] = $myFriend;
            $myFriend->friendsWithMe[] = $this;
        }
        
        return $this;
    }

    /**
     * @param User $myFriend
     */
    public function removeMyFriend(User $myFriend)
    {
        $this->myFriends->removeElement($myFriend);
        $myFriend->friendsWithMe->removeElement($this);
    }

    /**
     * @return ArrayCollection
     */
    public function getMyFriends()
    {
        return $this->myFriends;
    }
}

Контроллер

  1. Метод addMyFriend() -- или метод, в аннотации которого Вы указали описание таблицы friends
    $activeUser->addMyFriend($friend);
    • запишет idшники "друзей" в БД
    • можно будет достать друзей $activeUser-a так:
      $activeUser->getMyFriends();
    • можно будет найти activeUser-а среди "друзей" friend-а так:
      $friend->getFriendsWithMe();
    • Нельзя (будет пустым):
      • достать тех, кто "дружит" с activeUser-ом
        $activeUser->getFriendsWithMe();
      • достать всех "друзей" friend-а
        $friend->getMyFriends();
  2. Метод addFriendsWithMe() -- или метод, в аннотации которого Вы указали описание таблицы friends мы перенесли внутрь addMyFriend().
    • не запишет данные в БД, но добавляет объект пользователя в поле $FriendsWithMe
    • можно будет достать друзей пользователя friend, если для activeUser-a (и других друзей) был запущен метод addMyFriends() так:
      $friend->getFriendsWithMe()
  3. Обратите внимание на то, что если удалить данные из таблицы friends, данные в полях $MyFriends и $FriendsWithMe сохранятся, и все методы будут работать

ИТАК

В нашем случае для создания связей в контроллере целесообразно использовать методы добавления друзей в обе стороны:

$activeUser->addMyFriend($friend);
$friend->addMyFriend($activeUser);

Для удаления, соответственно, методы remove:

$activeUser->removeMyFriend($friend);
$friend->removeMyFriend($activeUser);

Возможно чтобы не было лишних методов, необходимо настраивать unidirectional (однонаправленную) связь.

vedro-compota's picture

Entity User и Контроллер лучше сделать H2 -- как и любые подзаголовки такого уровня. А не жирным

_____________
матфак вгу и остальная классика =)