Работа с доктриной. Многие ко многим: связываем посты с категориями
Привет! В этом уроке мы сделаем таблицу категорий для связывания их с постами. Давайте займемся определением отношения между категориями и публикациями. У одной категорий может быть много постов, как и у одной публикации - много категорий. Это называется отношением Многие ко Многим (Many-to-Many).
Чтобы определить связь, необходимо у сущности Post завести поле categories:
/**
* @var ArrayCollection
* @ORM\ManyToMany(targetEntity="App\Entity\Category", cascade={"persist"})
* @ORM\JoinTable(name="post_categories")
* @ORM\JoinColumn(referencedColumnName="id", nullable=false)
*/
private $categories;
Поле $categories будет типа ArrayCollection. В аннотации @ORM\ManyToMany мы указываем сущность для связи, в @ORM\JoinTable указываем имя пивот-таблицы, а @ORM\JoinColumn вам уже знакома. Можно такую же связь определить со стороны категории, чтобы через какой-нибудь $categories->getPosts()->toArray() достать все посты для конкретной категории, однако мы не будем так делать и не определим двухстороннюю связь. Вместо этого мы будем делать простой запрос через query builder.
Не забудем выполнить миграцию:
php bin/console doctrine:migrations:diff;
php bin/console doctrine:migrations:migrate;
После такого рефакторинга ваш проект перестанет работать, потому что репозитории у нас устроены по-старому. Давайте их немного перепишем:
<?php
declare(strict_types=1);
namespace App\Repository;
use Doctrine\ORM\EntityManagerInterface;
use Doctrine\ORM\EntityRepository;
use App\Entity\Post;
class PostRepository
{
/**
* @var EntityManagerInterface
*/
private $em;
/**
* @var EntityRepository
*/
private $repository;
public function __construct(EntityManagerInterface $em)
{
$this->em = $em;
$this->repository = $em->getRepository(Post::class);
}
public function add(Post $post)
{
$this->em->persist($post);
}
public function findOneBySlug(string $slug)
{
return $this->repository->findOneBy(['slug' => $slug]);
}
}
Теперь мы не наследуемся от ServiceEntityRepository; наш репозиторий занимается добавлением на сохранение в базу сущности; а также теперь у нас нет доступа напрямую к методам ServiceEntityRepository и желания сделать findAll() не возникнет. Все прежние методы вроде findOne, findOneBy, findAll доступны внутри репозитория и вы можете закрыть их своими методами, где явно определить, что и по какому полю вы достаете.
Так же поступаем и с репозиторием категории:
<?php
declare(strict_types=1);
namespace App\Repository;
use Doctrine\ORM\EntityManagerInterface;
use Doctrine\ORM\EntityRepository;
use App\Entity\Category;
class CategoryRepository
{
/**
* @var EntityManagerInterface
*/
private $em;
/**
* @var EntityRepository
*/
private $repository;
public function __construct(EntityManagerInterface $em)
{
$this->em = $em;
$this->repository = $em->getRepository(Category::class);
}
public function add(Category $category)
{
$this->em->persist($category);
}
public function findOneByName(string $name)
{
return $this->repository->findOneBy(['name' => $name]);
}
}
Скоро мы отрефакторим старые контроллеры, а также напишем новые. На следующем уроке мы займемся сохранением сущности поста вместе с его тегами, сохранением тега, редактирование поста, посмотрим на различные ситуации и то, как из них выйти. У уроку прикладываю гист, если вам лень переписывать.
Комментарии