Symfony 5.2:使用表单事件在动态修改表单中传递参数的问题

问题描述

我尝试应用文档 https://symfony.com/doc/current/form/dynamic_form_modification.html,但是当我收到第一个选择 categoryLevel1Id 以收集第二个选择 categoryLevel2Id 中的选项的结果时遇到了一些麻烦。

我是 Doctrine 的新手,我认为有些事情让我无法理解。

使用以下代码我有一个例外:

属性路径中给出的“int”、“object”类型的预期参数 "categoryLevel1Id"

            $builder->add('categoryLevel1Id',EntityType::class,[
                'class' => CategoryLevel1::class,'label' => 'contrib.create.category_level1','placeholder' => 'contrib.create.category_level1_placeholder','query_builder' => function (EntityRepository $er) {
                    return $er->createqueryBuilder('c')
                            ->andWhere("c.langId = ?1")
                            ->andWhere("c.validated = ?2")
                            ->setParameter(1,$this->langService->getLangIdByLang(locale_get_default()))
                            ->setParameter(2,1);
                },'choice_label' => 'title','choice_attr' => function($choice,$key,$value) {
                    return ['class' => 'text-dark'];
                },]);

使用以下代码我有此异常:

参数 2 传递给 App\Form\Front\Situ\CreateSituFormType::App\Form\Front\Situ{closure}() 必须是 App\Entity\CategoryLevel1 的一个实例或 null,int 给定, 在第 147 行的 ..\src\Form\Front\Situ\CreateSituFormType.PHP调用

$categories = $this->categoryLevel1Repository->findLocaleCategories();

            $categoriesOptions = [];
            foreach ($categories as $categorie) {
                $categoriesOptions[] = [
                    $categorie['title'] => $categorie['id'],];
            }
            $builder->add('categoryLevel1Id',ChoiceType::class,[
                'choices' => call_user_func_array('array_merge',$categoriesOptions),'label_attr' => ['class' => ''],]);

我尝试改为调用 CategoryLevel1Type::class,但是当代码来到 $formModifier = function (FormInterface $form.. 时它不起作用,或者我不知道改编代码

这是我的 buildForm() 之前有任何 __construct :

    public function buildForm(FormBuilderInterface $builder,array $options)
    {
        $builder
            ->add('title',TextType::class,[
                'label' => 'contrib.create.title','attr' => [
                    'class' => 'mb-md-4','placeholder' => 'contrib.create.title_placeholder'
                    ],])
            ->add('description',TextareaType::class,[
                'label' => 'contrib.create.description','attr' => [
                    'rows' => '5','placeholder' => 'contrib.create.description_placeholder',],])
        ;
        
        // Check locale categories level 1
        $categories = $this->categoryLevel1Repository->findLocaleCategories();
        
        // If no category,create it and its subcategory
        if (empty($categories)) {
            $builder
                ->add('categoryLevel1Id',CreateCategoryLevel1Type::class,[
                    'label' => 'category.create.category_level1','label_attr' => ['class' => 'pt-0'],])
                ->add('categoryLevel2Id',CreateCategoryLevel2Type::class,[
                    'label' => 'category.create.category_level2',])
            ; 
        } else {

            $builder->add('categoryLevel1Id',]);
                
            $formModifier = function (FormInterface $form,CategoryLevel1 $categoryLevel1 = null) {
                
                $categoriesLevel2 = null === $categoryLevel1 ? [] : 
                    $categoryLevel1->getGategoriesLevel2();
                                
                $form->add('categoryLevel2Id',[
                    'class' => 'App\Entity\CategoryLevel2','label' => 'contrib.create.category_level2','placeholder' => 'contrib.create.category_level2_placeholder','choices' => $categoriesLevel2,]);    
            };

            $builder->addEventListener(
                FormEvents::PRE_SET_DATA,function (FormEvent $event) use ($formModifier) {
                    // this would be your entity,i.e. SportMeetup
                    $data = $event->getData();
                    
                    $formModifier($event->getForm(),$data->getCategoryLevel1Id());

                }
            );

            $builder->get('categoryLevel1Id')->addEventListener(
                FormEvents::POST_SUBMIT,function (FormEvent $event) use ($formModifier) {
                    // It's important here to fetch $event->getForm()->getData(),as
                    // $event->getData() will get you the client data (that is,the ID)
                    $categoryLevel1 = $event->getForm()->getData();
                    // since we've added the listener to the child,we'll have to pass on
                    // the parent to the callback functions!
                    $formModifier($event->getForm()->getParent(),$categoryLevel1);    // (Error code 2 here)
                }
            );
        }
    }

    public function configureOptions(OptionsResolver $resolver)
    {
        $resolver->setDefaults([
            'data_class' => Situ::class,'translation_domain' => 'user_messages',]);
    }

然后是具有关系的实体:

类别级别 1:

    .../...
    
    /*
     * @ORM\OnetoMany(targetEntity="App\Entity\CategoryLevel2",cascade={"persist","remove"},mappedBy="categoryLevel1Id")
     */
    protected $categoriesLevel2;

    /*
    * @ORM\OnetoMany(targetEntity=Situ::class,mappedBy="categoryLevel1")
     */
    protected $situs;

    public function __construct()
    {
        $this->categoriesLevel2 = new ArrayCollection();
        $this->situs = new ArrayCollection();
    }

    .../...
    
    public function getGategoriesLevel2()
    {
        return $this->categoriesLevel2;
    }
     
    public function addCategoryLevel2(CategoryLevel2 $categoryLevel2)
    {
        $this->categoriesLevel2->add($categoryLevel2);
        $categoryLevel2->setCategoryLevel1($this);
    }
    
    public function getSitus()
    {
        return $this->situs;
    }
     
    public function addSitu(Situ $situ)
    {
        $this->situs->add($situ);
        $situ->setCategoryLevel1($this);
    }

类别级别 2:

    .../...

    /**
     * @ORM\Column(type="integer")
     * @ORM\ManyToOne(targetEntity="App\Entity\CategoryLevel1",inversedBy="categoriesLevel2")
     */
    private $categoryLevel1Id;

    /**
     * @ORM\Column(type="boolean")
     */
    private $validated;

    /**
    * @ORM\OnetoMany(targetEntity=Situ::class,mappedBy="categoryLevel2")
    */
    protected $situs;

    public function __construct()
    {
        $this->situs = new ArrayCollection();
    }

    .../...
    
    public function getSitus()
    {
        return $this->situs;
    }
     
    public function addSitu(Situ $situ)
    {
        $this->situs->add($situ);
        $situ->setCategoyLevel2($this);
    }
    .../...

现场:

    .../...

    /**
     * @ORM\Column(type="integer")
     */
    private $categoryLevel1Id;

    /**
     * @ORM\Column(type="integer")
     */
    private $categoryLevel2Id;

    /**
    * @ORM\ManyToOne(targetEntity=CategoryLevel1::class,inversedBy="situs")
    */
    protected $categoryLevel1;
    
    /**
    * @ORM\ManyToOne(targetEntity=CategoryLevel2::class,inversedBy="situs")
    */
    protected $categoryLevel2;

    .../...

进入 SituController,无非是:

    public function createSitu(Request $request,User $user): Response
    {
        $situ = new Situ();
        $form = $this->createForm(CreateSituFormType::class,$situ);
        $form->handleRequest($request);

        if ($form->isSubmitted() && $form->isValid()) {
            // Todo
        }
        
        return $this->render('front/situ/new.html.twig',[
            'situ' => $situ,'form' => $form->createView(),]);
    }

解决方法

您可能对这 2 个实体(CategoryLevel2 和 Situ)感到困惑:

  • CategoryLevel2 中是 App\Entity\CategoryLevel1 类型
  • Situ 中,它是 integer 类型

您不能在 EntityType::class 中使用 $builder->add('categoryLevel1Id',EntityType::class,[,因为在您的 CreateSituFormType 中,字段 categoryLevel1Id 是一个整数而不是实体。

在您的 Situ 实体中,您可能会更改:

/**
 * @ORM\Column(type="integer")
 */
private $categoryLevel1Id;

进入:

/**
 * @ORM\ManyToOne(targetEntity="App\Entity\CategoryLevel1")
 */
private $categoryLevel1Id;

解决问题。