Symfony 6 - 插入具有多对多关系的新条目时出错

发布于 2025-01-20 20:26:46 字数 8690 浏览 1 评论 0原文

我目前正在开发一个 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 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。
列表为空,暂无数据
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文