Created
January 9, 2023 20:06
-
-
Save revilon1991/ff300f9c6e57662cfc5e10dfa2c3c488 to your computer and use it in GitHub Desktop.
CRUD symfony form with ManyToMany relations
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?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(), | |
]); | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?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; | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?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; | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?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