Symfony 6 - 插入具有多对多关系的新条目时出错
我目前正在开发一个 symfony 6 项目,在该项目中我遇到了以下问题:
我必须输入“Article”和“ColorPalette”。两者之间的关系是多对多关系。
现在我有一个用于创建新文章的表单,我必须在其中为文章定义 1 到 n 种颜色。 select 字段使用 select2,它对于除多对多关系之外的所有关系都非常有效。因此,当我想提交表单时,出现以下错误:
“Doctrine\Common\Collections\ArrayCollection”类型的实体传递给 必须对选择字段进行管理。也许你忘记了坚持 实体经理?
我发现这与 colorPalette 的字段有关,它是一个多对多关系,但我不知道如何解决这个问题。
我当前的代码:
文章实体代码:
/**
* @ORM\Entity(repositoryClass=ArticleRepository::class)
*/
class Article
{
...other properties
/**
* @ORM\ManyToMany(targetEntity=ColorPalette::class, inversedBy="articles")
*/
private $colorPalette;
...other functions
/**
* @return Collection|ColorPalette[]
*/
public function getColorPalette(): Collection
{
return $this->colorPalette;
}
public function addColorPalette(ColorPalette $colorPalette): self
{
if (!$this->colorPalette->contains($colorPalette)) {
$this->colorPalette[] = $colorPalette;
}
return $this;
}
public function removeColorPalette(ColorPalette $colorPalette): self
{
$this->colorPalette->removeElement($colorPalette);
return $this;
}
}
ArticleType代码:
public function buildForm(FormBuilderInterface $builder, array $options): void
{
$builder
->add('title',null, array(
'label' => "title"
))
->add('titleEnglish',null, array(
'label' => "english title"
))
->add('description',null, array(
'label' => "description"
))
->add('stock',null, array(
'label' => "stock"
))
->add('status',ChoiceType::class, [
'label' => "status",
'choices' => [
'Gesperrt' => 'Gesperrt',
'Verfuegbar' => 'Verfuegbar',
'Auslaufartikel' => 'Auslaufartikel',
'Vergriffen' => 'Vergriffen'
],
'attr' => [
'class' => 'dropdown'
]
])
->add('colorPalette', EntityType::class, [
'class' => ColorPalette::class,
'multiple' => true,
'choice_label' => 'title',
'choices' => [],
'attr' => [
'class' => 'dropdown'
],
'label' => 'colors',
])
;
// Add EventSubscribers for specific fields (here: colorPalette)
$builder->addEventSubscriber(new EntityFieldListener($this->colorPaletteRepository,$this->colorPaletteClass,"colorPalette","colorPalette","id","title","colors"));
}
public function configureOptions(OptionsResolver $resolver): void
{
$resolver->setDefaults([
'data_class' => Article::class,
]);
}
EntityFieldListener代码:
class EntityFieldListener implements EventSubscriberInterface
{
public function __construct($repository="",$class="",$fieldName="",$table="",$identifier="",$choiceLabel="",$label="") {
$this->repository = $repository;
$this->class = $class;
$this->fieldName = $fieldName;
$this->table = $table;
$this->identifier = $identifier;
$this->choiceLabel = $choiceLabel;
$this->label = $label;
}
public static function getSubscribedEvents(): array {
return [
FormEvents::PRE_SET_DATA => 'onPreSetData',
FormEvents::PRE_SUBMIT => 'onPreSubmit',
];
}
//This Event is used to prepopulate the previous select options for all Select Fields in the corresponding form
public function onPreSetData(FormEvent $event, $test): void
{
// Get the parent form
$form = $event->getForm();
// Get the data for the choice field
$data = $event->getData();
if($data->getId() != null) {
// Get the Id of the currently selected Base Article for the query builder
$functionName = 'get'.ucfirst($this->fieldName);
$selected = $data->$functionName()->getId();
$form->add($this->fieldName, EntityType::class, array(
'class' => $this->class,
'choice_label' => $this->choiceLabel,
'label' => $this->label,
'attr' => [
'class' => 'dropdown'
],
'query_builder' => function () use ($selected){
return $this->repository->createQueryBuilder($this->table)
->where($this->table.'.'.$this->identifier.' = :'.$this->identifier)
->setParameter($this->identifier, $selected);
},
));
}
}
public function onPreSubmit(FormEvent $event) {
// Get the parent form
$form = $event->getForm();
// Get the data for the choice field
$data = $event->getData();
if(isset($data[$this->fieldName]) and $data[$this->fieldName]!=null){
$selected = $data[$this->fieldName];
$form->add($this->fieldName, EntityType::class, array(
'class' => $this->class,
'choice_label' => $this->choiceLabel,
'label' => $this->label,
'attr' => [
'class' => 'dropdown'
],
'query_builder' => function () use ($selected){
//If a parameter is an array then this should be used:
// if(is_array($selected)) {
// $query = $this->repository->createQueryBuilder($this->table);
// $searchQuery="";
// for($i = 0; $i < count($selected); $i++) {
// if($i < count($selected)-1) {
// $searchQuery .= $this->table.'.'.$this->identifier.' = '.$selected[$i].' OR ';
// }
// else {
// $searchQuery .= $this->table.'.'.$this->identifier.' = '.$selected[$i];
// }
// }
// $query->andWhere($searchQuery);
// return $query;
// }
return $this->repository->createQueryBuilder($this->table)
// ->where($this->table.'.id = :id')
->where($this->table.'.'.$this->identifier.' = :'.$this->identifier)
->setParameter($this->identifier, $selected);
},
));
}
}
}
控制器代码(仅插入新条目的路由):
#[Route('/new', name: 'new', methods: ['GET', 'POST'])]
public function new(Request $request, EntityManagerInterface $entityManager): Response
{
$article = new Article();
$form = $this->createForm(ArticleType::class, $article);
$form->handleRequest($request);
if ($form->isSubmitted() && $form->isValid()) {
//insert the new article
$this->entityManager->persist($article);
$this->entityManager->flush();
return $this->redirectToRoute($this->routeName.'_index', [], Response::HTTP_SEE_OTHER);
}
return $this->renderForm($this->routeName.'/new.html.twig', [
$this->routeName => $article,
'form' => $form,
]);
}
JS代码:
//Imports
import {createSelectPicker} from './components/Select2/SelectPicker';
//Document Ready Function
$(function() {
document.getElementById("page-content").style.visibility = "visible";
createSelectPicker({id:'#article_colorPalette',minimumInputLength:1,multiple:true,selectField:'colorPalette',url:url_from_twig_ajax});
} );
您知道如何解决这个问题吗?
我将不胜感激任何帮助。 :)
I am currently working on a symfony 6 project where I came across the following problem:
I have to entites "Article" and "ColorPalette". The relationship between the both of these is a ManyToMany Relationship.
Now I have a form for creating new Articles where I have to define between 1 and n colors for the article. The select field uses select2 which works perfectly fine for all relationships beside the many to many one. So when I want to submit the form I get the following error:
Entity of type "Doctrine\Common\Collections\ArrayCollection" passed to
the choice field must be managed. Maybe you forget to persist it in
the entity manager?
I was able to find out that this has something to do with the field for the colorPalette which is a ManyToMany Relation but I could not figure out how to solve the issue.
My Current Code:
Article Entity Code:
/**
* @ORM\Entity(repositoryClass=ArticleRepository::class)
*/
class Article
{
...other properties
/**
* @ORM\ManyToMany(targetEntity=ColorPalette::class, inversedBy="articles")
*/
private $colorPalette;
...other functions
/**
* @return Collection|ColorPalette[]
*/
public function getColorPalette(): Collection
{
return $this->colorPalette;
}
public function addColorPalette(ColorPalette $colorPalette): self
{
if (!$this->colorPalette->contains($colorPalette)) {
$this->colorPalette[] = $colorPalette;
}
return $this;
}
public function removeColorPalette(ColorPalette $colorPalette): self
{
$this->colorPalette->removeElement($colorPalette);
return $this;
}
}
ArticleType Code:
public function buildForm(FormBuilderInterface $builder, array $options): void
{
$builder
->add('title',null, array(
'label' => "title"
))
->add('titleEnglish',null, array(
'label' => "english title"
))
->add('description',null, array(
'label' => "description"
))
->add('stock',null, array(
'label' => "stock"
))
->add('status',ChoiceType::class, [
'label' => "status",
'choices' => [
'Gesperrt' => 'Gesperrt',
'Verfuegbar' => 'Verfuegbar',
'Auslaufartikel' => 'Auslaufartikel',
'Vergriffen' => 'Vergriffen'
],
'attr' => [
'class' => 'dropdown'
]
])
->add('colorPalette', EntityType::class, [
'class' => ColorPalette::class,
'multiple' => true,
'choice_label' => 'title',
'choices' => [],
'attr' => [
'class' => 'dropdown'
],
'label' => 'colors',
])
;
// Add EventSubscribers for specific fields (here: colorPalette)
$builder->addEventSubscriber(new EntityFieldListener($this->colorPaletteRepository,$this->colorPaletteClass,"colorPalette","colorPalette","id","title","colors"));
}
public function configureOptions(OptionsResolver $resolver): void
{
$resolver->setDefaults([
'data_class' => Article::class,
]);
}
EntityFieldListener Code:
class EntityFieldListener implements EventSubscriberInterface
{
public function __construct($repository="",$class="",$fieldName="",$table="",$identifier="",$choiceLabel="",$label="") {
$this->repository = $repository;
$this->class = $class;
$this->fieldName = $fieldName;
$this->table = $table;
$this->identifier = $identifier;
$this->choiceLabel = $choiceLabel;
$this->label = $label;
}
public static function getSubscribedEvents(): array {
return [
FormEvents::PRE_SET_DATA => 'onPreSetData',
FormEvents::PRE_SUBMIT => 'onPreSubmit',
];
}
//This Event is used to prepopulate the previous select options for all Select Fields in the corresponding form
public function onPreSetData(FormEvent $event, $test): void
{
// Get the parent form
$form = $event->getForm();
// Get the data for the choice field
$data = $event->getData();
if($data->getId() != null) {
// Get the Id of the currently selected Base Article for the query builder
$functionName = 'get'.ucfirst($this->fieldName);
$selected = $data->$functionName()->getId();
$form->add($this->fieldName, EntityType::class, array(
'class' => $this->class,
'choice_label' => $this->choiceLabel,
'label' => $this->label,
'attr' => [
'class' => 'dropdown'
],
'query_builder' => function () use ($selected){
return $this->repository->createQueryBuilder($this->table)
->where($this->table.'.'.$this->identifier.' = :'.$this->identifier)
->setParameter($this->identifier, $selected);
},
));
}
}
public function onPreSubmit(FormEvent $event) {
// Get the parent form
$form = $event->getForm();
// Get the data for the choice field
$data = $event->getData();
if(isset($data[$this->fieldName]) and $data[$this->fieldName]!=null){
$selected = $data[$this->fieldName];
$form->add($this->fieldName, EntityType::class, array(
'class' => $this->class,
'choice_label' => $this->choiceLabel,
'label' => $this->label,
'attr' => [
'class' => 'dropdown'
],
'query_builder' => function () use ($selected){
//If a parameter is an array then this should be used:
// if(is_array($selected)) {
// $query = $this->repository->createQueryBuilder($this->table);
// $searchQuery="";
// for($i = 0; $i < count($selected); $i++) {
// if($i < count($selected)-1) {
// $searchQuery .= $this->table.'.'.$this->identifier.' = '.$selected[$i].' OR ';
// }
// else {
// $searchQuery .= $this->table.'.'.$this->identifier.' = '.$selected[$i];
// }
// }
// $query->andWhere($searchQuery);
// return $query;
// }
return $this->repository->createQueryBuilder($this->table)
// ->where($this->table.'.id = :id')
->where($this->table.'.'.$this->identifier.' = :'.$this->identifier)
->setParameter($this->identifier, $selected);
},
));
}
}
}
Controller Code (Only the route for inserting new entries):
#[Route('/new', name: 'new', methods: ['GET', 'POST'])]
public function new(Request $request, EntityManagerInterface $entityManager): Response
{
$article = new Article();
$form = $this->createForm(ArticleType::class, $article);
$form->handleRequest($request);
if ($form->isSubmitted() && $form->isValid()) {
//insert the new article
$this->entityManager->persist($article);
$this->entityManager->flush();
return $this->redirectToRoute($this->routeName.'_index', [], Response::HTTP_SEE_OTHER);
}
return $this->renderForm($this->routeName.'/new.html.twig', [
$this->routeName => $article,
'form' => $form,
]);
}
JS-Code:
//Imports
import {createSelectPicker} from './components/Select2/SelectPicker';
//Document Ready Function
$(function() {
document.getElementById("page-content").style.visibility = "visible";
createSelectPicker({id:'#article_colorPalette',minimumInputLength:1,multiple:true,selectField:'colorPalette',url:url_from_twig_ajax});
} );
Do you have an Idea how to solve this?
I would appreciate any help. :)
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论