symfony Doctrine -- Связанные сущности -- Пример One To Many связи (с использованием ArrayCollection)
Primary tabs
Здесь описан пример, как настроить связь между таблицами House (Дома) и User (Жильцы) -- One To Many (один ко многим), и чтобы она работала. Можно будет обращаться к объекту Дома через объект Жильца, и наоборот.
Создаём связанные таблицы с помощью аннотаций в Entity :
Класс User (Жильцы):
<?php
namespace AppBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
/**
* @ORM\Entity
* @ORM\Table(name="`user`")
*/
class User
{
/**
* @var House
*
* @ORM\ManyToOne(targetEntity="House", inversedBy="users")
* @ORM\JoinColumn(name="house_id", referencedColumnName="id")
*/
protected $house;
/**
* Узнать, к какому дому относится данный юзер (когда уже связаны)
*
* @return House
*/
public function getHouse(): House
{
return $this->house;
}
/**
* Установить связь с таблицей домов (когда ещё не связаны)
*
* @param House $house
*
* @return self
*/
public function setHouse(House $house): self
{
$this->house = $house;
return $this;
}
}Класс House (Дома):
<?php
namespace AppBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
use Doctrine\Common\Collections\ArrayCollection;
/**
* @ORM\Entity
* @ORM\Table(name="`house`")
*/
class House {
/**
* @var ArrayCollection
*
* @ORM\OneToMany(targetEntity="User", mappedBy="house")
*/
protected $users;
public function __construct()
{
$this->users = new ArrayCollection();
}
/**
* @param User $user
*
* @return self
*/
public function addUser(User $user): self
{
if (!$this->users->contains($user)) { // проверяем, есть ли уже наш юзер в коллекции дома (в свойстве users)
$user->setHouse($this); // устанавливаем связь между таблицами
$this->users->add($user); // добавляем юзера в коллекцию (в свойство users)
}
return $this;
}
/**
* @param User $user
*
* @return self
*/
public function removeUser(User $user): self
{
if ($this->users->contains($user)) { // проверяем, есть ли наш юзер в коллекции дома (в свойстве users)
$this->users->removeElement($user); // удаляем его из коллекции (из свойства users)
}
return $this;
}
/**
* @return ArrayCollection
*/
public function getUsers(): ArrayCollection
{
return $this->users; // получить список всех юзеров в коллекции (в свойстве users)
}
}
Как можно заметить, аннотации настроены в полном соответствии с документацией Symfony.
У классов есть св-ва $house и $users соответственно, с помощью которых мы будем работать в контроллере.
В базе данных таблицы связаны через поля house_id и id соответственно, что указано в аннотациях.
Теперь что касается методов.
- Для сущности, объектов которой предполагается много (в данном случае - User) создаём стандартные геттер и сеттер для свойства $house.
- Для сущности, объект которой один будет иметь связи со многими (в данном случае House), свойство $users - объект ArrayCollection. Это определяется в конструкторе класса. О том, кто такой ArrayCollection, для чего он нужен и с чем его едят, можно почитать здесь.
Создаём методы:
addUser() - добавляет юзера в коллекцию (в свойство users) и устанавливает связь между таблицами User и House,
removeUser() - удаляет юзера из коллекции,
getUsers() - возвращает список всех юзеров данного дома.
Как это использовать в контроллере
Связывать сущности можно как в направлении одна ко многим(с помощью setProp()), так и в обратном(с помощью addProp()). Но рекомендуется привязывать многие к одной (второй вариант), т.к. это красивее с точки зрения здравого смысла:
"к этому Дому привязываем ещё одного Жильца"
==
$house->addUser($user);
Итак, примеры:
Контроллер 1 (Более общий вариант. Жилец и Дом уже существуют и связаны. Добавляем новые данные о Жильце и Доме, Жильца "узнаём" из сессии, а Дом - с помощью связанных таблиц):
// получаем данные из формы
$formData = $request->request->all();
// получаем Жильца из сессии
$user = $this->getUser();
$user->setEmail($formData['app_meeting_registration']['email']);
// достаём объект Дома, к которому привязан Жилец, и заполняем Дом данными
$house = $user->getHouse();
$house->setFirstFlatNumber($formData['app_meeting_registration']['firstFlatNumber']);
$house->setLastFlatNumber($formData['app_meeting_registration']['lastFlatNumber']);
// сохраняем оба обновлённых объекта в БД
$em = $this->getDoctrine()->getManager();
$em->persist($house);
$em->persist($user);
$em->flush();
Контроллер 2 (Происходит регистрация Жилеца. Проверяем, есть ли уже в базе Дом, который указан Жильцом. Если есть - привязываем, если нет - создаём и привязываем.):
$userManager->updateUser($user); // создание нового юзера (при регистрации)
// получаем Адрес из формы для сохранения в House
$formData = $request->request->all();
$houseAddress = $formData['app_user_registration']['address'];
// проверяем, есть ли Дом с таким Адресом в нашей базе
$house = $this->getDoctrine()->getManager()->getRepository(House::class)
->findOneBy(['address' => $houseAddress]);
// если нет, создаём новый
if (empty($house)) {
$house = new House();
}
// создаём связь (по факту, заполняется поле house_id, являющееся foreign key-ем)
$house->addUser($user); // Вариант предпочтительный. Доступ от одного ко многим
// $user->setHouse($house); // Тоже сработает. Доступ от многих к одному
$house->setAddress($formData['app_user_registration']['address']);
// сохраняем изменения в БД
$em = $this->getDoctrine()
->getManager();
$em->persist($house);
$em->flush();Источники
Работа с коллекциями форм:
http://symfony.com/doc/current/form/form_collections.html
https://stackoverflow.com/questions/25085276/symfony2-multiple-entities-one-form
Связаные сущности:
https://symfony.com/doc/current/doctrine/associations.html#content_wrapper
ArrayCollection:
https://habrahabr.ru/post/127711/
- Log in to post comments
- 8969 reads
vedro-compota
Tue, 11/07/2017 - 16:11
Permalink
"источники" лучше оформлять
"источники" лучше оформлять как список
_____________
матфак вгу и остальная классика =)