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...
https://stackoverflow.com/questions/2508...
Связаные сущности:
https://symfony.com/doc/current/doctrine...
ArrayCollection:
https://habrahabr.ru/post/127711/
- Log in to post comments
- 7954 reads
vedro-compota
Tue, 11/07/2017 - 16:11
Permalink
"источники" лучше оформлять
"источники" лучше оформлять как список
_____________
матфак вгу и остальная классика =)