January 7

Связь Many-to-Many в Symfony

При проектировании баз данных и работе с фреймворком Symfony, одной из самых распространённых задач является создание связи типа "многие ко многим" (Many-to-Many) между сущностями. Рассмотрим сценарий, где у нас есть две сущности: User (пользователь) и Group (группа). Каждый пользователь может принадлежать к нескольким группам, а каждая группа может содержать несколько пользователей. Это классический пример связи Many-to-Many.

Как реализовать связь Many-to-Many в Symfony

В Symfony связь Many-to-Many реализуется с использованием аннотаций Doctrine ORM. Давайте рассмотрим, как можно настроить такую связь и вывести данные из базы.


1. Создание сущностей

Сущность User

<?php

namespace App\Entity;

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

#[ORM\Entity]
class User
{
    #[ORM\Id]
    #[ORM\GeneratedValue]
    #[ORM\Column(type: 'integer')]
    private int $id;

    #[ORM\Column(type: 'string', length: 255)]
    private string $name;

    #[ORM\ManyToMany(targetEntity: Group::class, inversedBy: 'users')]
    #[ORM\JoinTable(name: 'user_group')]
    private Collection $groups;

    public function __construct()
    {
        $this->groups = new ArrayCollection();
    }

    // Геттеры и сеттеры

    public function getId(): int
    {
        return $this->id;
    }

    public function getName(): string
    {
        return $this->name;
    }

    public function setName(string $name): self
    {
        $this->name = $name;

        return $this;
    }

    public function getGroups(): Collection
    {
        return $this->groups;
    }

    public function addGroup(Group $group): self
    {
        if (!$this->groups->contains($group)) {
            $this->groups[] = $group;
            $group->addUser($this);
        }

        return $this;
    }

    public function removeGroup(Group $group): self
    {
        if ($this->groups->removeElement($group)) {
            $group->removeUser($this);
        }

        return $this;
    }
}

Сущность Group

<?php

namespace App\Entity;

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

#[ORM\Entity]
class Group
{
    #[ORM\Id]
    #[ORM\GeneratedValue]
    #[ORM\Column(type: 'integer')]
    private int $id;

    #[ORM\Column(type: 'string', length: 255)]
    private string $name;

    #[ORM\ManyToMany(targetEntity: User::class, mappedBy: 'groups')]
    private Collection $users;

    public function __construct()
    {
        $this->users = new ArrayCollection();
    }

    // Геттеры и сеттеры

    public function getId(): int
    {
        return $this->id;
    }

    public function getName(): string
    {
        return $this->name;
    }

    public function setName(string $name): self
    {
        $this->name = $name;

        return $this;
    }

    public function getUsers(): Collection
    {
        return $this->users;
    }

    public function addUser(User $user): self
    {
        if (!$this->users->contains($user)) {
            $this->users[] = $user;
        }

        return $this;
    }

    public function removeUser(User $user): self
    {
        $this->users->removeElement($user);

        return $this;
    }
}

2. Миграции

После того как мы создали сущности, необходимо сгенерировать и выполнить миграцию базы данных:

php bin/console make:migration
php bin/console doctrine:migrations:migrate

3. Пример с таблицами и тестовыми данными

Для того чтобы продемонстрировать работу с сущностями, давайте создадим таблицы и заполним их тестовыми данными. В базе данных будут следующие таблицы:

4. Пример работы с данными

Теперь давайте выведем группы для пользователя с ID = 1. Выполнив следующий код, мы получим:

$userRepository = $entityManager->getRepository(User::class);
$user = $userRepository->find(1);

// Выводим группы пользователя
foreach ($user->getGroups() as $group) {
    echo $group->getName();
}

Ожидаемый вывод для пользователя с ID = 1 (Иван Иванов):

Администраторы
Модераторы

Для пользователя с ID = 4 (Мария Смирнова) вывод будет:

Модераторы
Разработчики
Сотрудники

5. Заключение

Связь Many-to-Many в Symfony — это мощный инструмент, который позволяет организовать отношения между сущностями