Skip to content

Instantly share code, notes, and snippets.

@revilon1991
Created January 9, 2023 20:06
Show Gist options
  • Save revilon1991/ff300f9c6e57662cfc5e10dfa2c3c488 to your computer and use it in GitHub Desktop.
Save revilon1991/ff300f9c6e57662cfc5e10dfa2c3c488 to your computer and use it in GitHub Desktop.
CRUD symfony form with ManyToMany relations
<?php
declare(strict_types=1);
namespace App\Controller;
use App\Entity\Article;
use App\Entity\ArticleHasTag;
use App\Entity\Tag;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\ORM\EntityManagerInterface;
use Symfony\Bridge\Doctrine\Form\Type\EntityType;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\Form\Extension\Core\Type\SubmitType;
use Symfony\Component\Form\Extension\Core\Type\TextType;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
use Symfony\Component\Routing\Annotation\Route;
class AppController extends AbstractController
{
public function __construct(private readonly EntityManagerInterface $entityManager)
{
}
#[Route(path: '/article', methods: ['GET', 'POST'])]
public function articleCreate(Request $request): Response
{
$article = new Article();
$formBuilder = $this->createFormBuilder($article, [
'data_class' => Article::class,
]);
$formBuilder->add('title', TextType::class);
$formBuilder->add('articleHasTagList', EntityType::class, [
'class' => ArticleHasTag::class,
'choice_label' => 'tag.name',
'choice_value' => 'tag.id',
'multiple' => true,
'choices' => (function (Article $article) {
$tags = $this->entityManager->getRepository(Tag::class)->findAll();
$articleHasTagList = [];
foreach ($tags as $tag) {
$articleHasTag = new ArticleHasTag();
$articleHasTag->setArticle($article);
$articleHasTag->setTag($tag);
$articleHasTagList[] = $articleHasTag;
}
return $articleHasTagList;
})($article),
]);
$formBuilder->add('submit', SubmitType::class);
$form = $formBuilder->getForm();
$form->handleRequest($request);
if ($form->isSubmitted() && $form->isValid()) {
$this->entityManager->persist($article);
foreach ($article->getArticleHasTagList() as $articleHasTag) {
$this->entityManager->persist($articleHasTag);
}
$this->entityManager->flush();
return $this->redirectToRoute('app_app_articleedit', [
'articleId' => $article->getId(),
]);
}
return $this->render('base.html.twig', [
'form' => $form->createView(),
]);
}
#[Route(path: '/article/{articleId}', methods: ['GET', 'POST'])]
public function articleEdit(string $articleId, Request $request): Response
{
$article = $this->entityManager->find(Article::class, $articleId);
if (!$article) {
throw new NotFoundHttpException();
}
$originalTags = new ArrayCollection($article->getArticleHasTagList()->toArray());
$formBuilder = $this->createFormBuilder($article, [
'data_class' => Article::class,
]);
$formBuilder->add('title', TextType::class);
$formBuilder->add('articleHasTagList', EntityType::class, [
'class' => ArticleHasTag::class,
'choice_label' => 'tag.name',
'choice_value' => 'tag.id',
'multiple' => true,
'choices' => (function (Article $article) {
$articleHasTagList = clone $article->getArticleHasTagList();
$tags = $this->entityManager->getRepository(Tag::class)->findAll();
foreach ($tags as $tag) {
/** @var ArticleHasTag[] $articleHasTagList */
foreach ($articleHasTagList as $articleHasTag) {
if ($tag === $articleHasTag->getTag()) {
continue 2;
}
}
$articleHasTag = new ArticleHasTag();
$articleHasTag->setArticle($article);
$articleHasTag->setTag($tag);
$articleHasTagList->add($articleHasTag);
}
return $articleHasTagList;
})($article),
]);
$formBuilder->add('submit', SubmitType::class);
$form = $formBuilder->getForm();
$form->handleRequest($request);
if ($form->isSubmitted() && $form->isValid()) {
$this->entityManager->persist($article);
/** @var ArticleHasTag[] $originalTags */
foreach ($originalTags as $articleHasTag) {
if (!$article->getArticleHasTagList()->contains($articleHasTag)) {
$this->entityManager->remove($articleHasTag);
}
}
foreach ($article->getArticleHasTagList() as $articleHasTag) {
$this->entityManager->persist($articleHasTag);
}
$this->entityManager->flush();
return $this->redirectToRoute('app_app_articleedit', [
'articleId' => $articleId,
]);
}
return $this->render('base.html.twig', [
'form' => $form->createView(),
]);
}
}
<?php
declare(strict_types=1);
namespace App\Entity;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\Common\Collections\Collection;
use Doctrine\ORM\Mapping\Column;
use Doctrine\ORM\Mapping\Entity;
use Doctrine\ORM\Mapping\GeneratedValue;
use Doctrine\ORM\Mapping\Id;
use Doctrine\ORM\Mapping\OneToMany;
#[Entity]
class Article
{
#[Id]
#[Column(type: 'bigint')]
#[GeneratedValue]
private string $id;
#[Column]
private string $title;
/**
* @var Collection<int, ArticleHasTag>
*/
#[OneToMany(mappedBy: 'article', targetEntity: ArticleHasTag::class, fetch: 'EAGER')]
private Collection $articleHasTagList;
public function __construct()
{
$this->articleHasTagList = new ArrayCollection();
}
public function getId(): string
{
return $this->id;
}
public function setId(string $id): void
{
$this->id = $id;
}
public function getTitle(): string
{
return $this->title;
}
public function setTitle(string $title): void
{
$this->title = $title;
}
public function addArticleHasTagList(ArticleHasTag $articleHasTag): void
{
$articleHasTag->getTag()->addTagHasArticleList($articleHasTag);
$articleHasTag->setArticle($this);
$this->articleHasTagList->add($articleHasTag);
}
public function removeArticleHasTagList(ArticleHasTag $articleHasTag): void
{
$articleHasTag->getTag()->removeTagHasArticleList($articleHasTag);
$this->articleHasTagList->removeElement($articleHasTag);
}
public function getArticleHasTagList(): Collection
{
return $this->articleHasTagList;
}
}
<?php
declare(strict_types=1);
namespace App\Entity;
use Doctrine\ORM\Mapping\Column;
use Doctrine\ORM\Mapping\Entity;
use Doctrine\ORM\Mapping\GeneratedValue;
use Doctrine\ORM\Mapping\Id;
use Doctrine\ORM\Mapping\Index;
use Doctrine\ORM\Mapping\JoinColumn;
use Doctrine\ORM\Mapping\ManyToOne;
use Doctrine\ORM\Mapping\OneToMany;
use Doctrine\ORM\Mapping\Table;
use Doctrine\ORM\Mapping\UniqueConstraint;
#[Entity]
#[UniqueConstraint(name: 'uniqArticleIdTagId', columns: ['article_id', 'tag_id'])]
#[Index(columns: ['article_id'], name: 'idxArticleId')]
#[Index(columns: ['tag_id'], name: 'idxTagId')]
class ArticleHasTag
{
#[Id]
#[Column(type: 'bigint')]
#[GeneratedValue]
private string $id;
#[ManyToOne(targetEntity: Article::class, fetch: 'EAGER', inversedBy: 'articleHasTagList')]
#[JoinColumn(nullable: false)]
private Article $article;
#[ManyToOne(targetEntity: Tag::class, fetch: 'EAGER', inversedBy: 'tagHasArticleList')]
#[JoinColumn(nullable: false)]
private Tag $tag;
public function getId(): string
{
return $this->id;
}
public function setId(string $id): void
{
$this->id = $id;
}
public function getArticle(): Article
{
return $this->article;
}
public function setArticle(Article $article): void
{
$this->article = $article;
}
public function getTag(): Tag
{
return $this->tag;
}
public function setTag(Tag $tag): void
{
$this->tag = $tag;
}
}
<?php
declare(strict_types=1);
namespace App\Entity;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\Common\Collections\Collection;
use Doctrine\ORM\Mapping\Column;
use Doctrine\ORM\Mapping\Entity;
use Doctrine\ORM\Mapping\GeneratedValue;
use Doctrine\ORM\Mapping\Id;
use Doctrine\ORM\Mapping\OneToMany;
#[Entity]
class Tag
{
#[Id]
#[Column(type: 'bigint')]
#[GeneratedValue]
private string $id;
#[Column]
private string $name;
/**
* @var Collection<int, ArticleHasTag>
*/
#[OneToMany(mappedBy: 'tag', targetEntity: ArticleHasTag::class, fetch: 'EAGER')]
private Collection $tagHasArticleList;
public function getId(): string
{
return $this->id;
}
public function setId(string $id): void
{
$this->id = $id;
}
public function getName(): string
{
return $this->name;
}
public function setName(string $name): void
{
$this->name = $name;
}
public function __construct()
{
$this->tagHasArticleList = new ArrayCollection();
}
public function addTagHasArticleList(ArticleHasTag $articleHasTag): void
{
if (!$this->tagHasArticleList->contains($articleHasTag)) {
$this->tagHasArticleList->add($articleHasTag);
}
}
public function removeTagHasArticleList(ArticleHasTag $articleHasTag): void
{
if ($this->tagHasArticleList->contains($articleHasTag)) {
$this->tagHasArticleList->removeElement($articleHasTag);
}
}
public function getTagHasArticleList(): Collection
{
return $this->tagHasArticleList;
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment