Symfony 5.2: проблемы с аргументом, переданным в форме динамической модификации с использованием событий формы

#php #symfony #doctrine-orm #doctrine-query

#php #symfony #doctrine-orm #доктрина-запрос

Вопрос:

Я пытаюсь применить документацию 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, передаваемый в AppFormFrontSituCreateSituFormType::AppFormFrontSitu{closure}(), должен быть экземпляром AppEntity CategoryLevel1 или null, заданный int, вызывается ..srcFormFrontSituCreateSituFormType.php в строке 147

 $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' => 'contrib.create.category_level1',
                'label_attr' => ['class' => ''],
                'placeholder' => 'contrib.create.category_level1_placeholder',
                'choice_attr' => function($choice, $key, $value) {
                    return ['class' => 'text-dark'];
                },
            ]);
 

Вместо этого я попытался вызвать CategoryLevel1Type::class, но это не сработало, когда пришел код $formModifier = function (FormInterface $form.. , конечно, или я не знал, как адаптировать код!

Это моя buildForm() с любой __конструкцией перед:

     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',
                    'label_attr' => ['class' => 'pt-0'],
                ])
            ; 
        } else {

            $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'];
                },
            ]);
                
            $formModifier = function (FormInterface $form, CategoryLevel1 $categoryLevel1 = null) {
                
                $categoriesLevel2 = null === $categoryLevel1 ? [] : 
                    $categoryLevel1->getGategoriesLevel2();
                                
                $form->add('categoryLevel2Id', EntityType::class, [
                    'class' => 'AppEntityCategoryLevel2',
                    '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',
        ]);
    }
 

Then Entities with Relations:

CategoryLevel1:

     .../...
    
    /*
     * @ORMOneToMany(targetEntity="AppEntityCategoryLevel2", cascade={"persist", "remove"}, mappedBy="categoryLevel1Id")
     */
    protected $categoriesLevel2;

    /*
    * @ORMOneToMany(targetEntity=Situ::class, cascade={"persist", "remove"}, 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);
    }

 

CategoryLevel2:

     .../...

    /**
     * @ORMColumn(type="integer")
     * @ORMManyToOne(targetEntity="AppEntityCategoryLevel1", inversedBy="categoriesLevel2")
     */
    private $categoryLevel1Id;

    /**
     * @ORMColumn(type="boolean")
     */
    private $validated;

    /**
    * @ORMOneToMany(targetEntity=Situ::class, cascade={"persist", "remove"}, 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);
    }
    .../...
 

Situ :

     .../...

    /**
     * @ORMColumn(type="integer")
     */
    private $categoryLevel1Id;

    /**
     * @ORMColumn(type="integer")
     */
    private $categoryLevel2Id;

    /**
    * @ORMManyToOne(targetEntity=CategoryLevel1::class, inversedBy="situs")
    */
    protected $categoryLevel1;
    
    /**
    * @ORMManyToOne(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() amp;amp; $form->isValid()) {
            // TODO
        }
        
        return $this->render('front/situ/new.html.twig', [
            'situ' => $situ,
            'form' => $form->createView(),
        ]);
    }
 

Ответ №1:

Вы, вероятно, запутались между этими 2 объектами (CategoryLevel2 и Situ):

  • В CategoryLevel2 этом AppEntityCategoryLevel1 типе
  • в то время как в Situ это integer тип

Вы не можете использовать EntityType::class in $builder->add('categoryLevel1Id', EntityType::class, [ , потому что в вашем CreateSituFormType поле categoryLevel1Id является целым числом, а не объектом.

В вашей Situ сущности вы можете изменить:

 /**
 * @ORMColumn(type="integer")
 */
private $categoryLevel1Id;
 

в:

 /**
 * @ORMManyToOne(targetEntity="AppEntityCategoryLevel1")
 */
private $categoryLevel1Id;
 

для решения проблемы.