Symfony 5.2:使用表单事件在动态修改表单中传递参数的问题
Symfony 5.2 : Problems with Argument passed in Dynamically Modifification Form Using Form Events
我尝试应用文档 https://symfony.com/doc/current/form/dynamic_form_modification.html 但是当我收到第一个 select categoryLevel1Id
的结果以在第二个 select 中收集选项时我遇到了一些麻烦 categoryLevel2Id
.
我是 Doctrine 的新手,我觉得有些事情让我无法理解。
使用下面的代码,我有一个例外:
Expected argument of type "int", "object" given at property path
"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'];
},
]);
使用以下代码,我有这个例外:
Argument 2 passed to
App\Form\Front\Situ\CreateSituFormType::App\Form\Front\Situ{closure}()
must be an instance of App\Entity\CategoryLevel1 or null, int given,
called in ..\src\Form\Front\Situ\CreateSituFormType.php on line 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(),之前有任何 __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',
'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' => '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, 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);
}
类别级别 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, 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);
}
.../...
现场:
.../...
/**
* @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(),
]);
}
您可能对这两个实体(CategoryLevel2 和 Situ)感到困惑:
- 在
CategoryLevel2
中是App\Entity\CategoryLevel1
类型
- 而在
Situ
中是 integer
类型
您不能在 $builder->add('categoryLevel1Id', EntityType::class, [
中使用 EntityType::class
,因为在您的 CreateSituFormType
中,字段 categoryLevel1Id
是整数而不是实体。
在您的 Situ
实体中,您可能会更改:
/**
* @ORM\Column(type="integer")
*/
private $categoryLevel1Id;
进入:
/**
* @ORM\ManyToOne(targetEntity="App\Entity\CategoryLevel1")
*/
private $categoryLevel1Id;
解决问题
我尝试应用文档 https://symfony.com/doc/current/form/dynamic_form_modification.html 但是当我收到第一个 select categoryLevel1Id
的结果以在第二个 select 中收集选项时我遇到了一些麻烦 categoryLevel2Id
.
我是 Doctrine 的新手,我觉得有些事情让我无法理解。
使用下面的代码,我有一个例外:
Expected argument of type "int", "object" given at property path "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'];
},
]);
使用以下代码,我有这个例外:
Argument 2 passed to App\Form\Front\Situ\CreateSituFormType::App\Form\Front\Situ{closure}() must be an instance of App\Entity\CategoryLevel1 or null, int given, called in ..\src\Form\Front\Situ\CreateSituFormType.php on line 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(),之前有任何 __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',
'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' => '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, 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);
}
类别级别 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, 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);
}
.../...
现场:
.../...
/**
* @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(),
]);
}
您可能对这两个实体(CategoryLevel2 和 Situ)感到困惑:
- 在
CategoryLevel2
中是App\Entity\CategoryLevel1
类型 - 而在
Situ
中是integer
类型
您不能在 $builder->add('categoryLevel1Id', EntityType::class, [
中使用 EntityType::class
,因为在您的 CreateSituFormType
中,字段 categoryLevel1Id
是整数而不是实体。
在您的 Situ
实体中,您可能会更改:
/**
* @ORM\Column(type="integer")
*/
private $categoryLevel1Id;
进入:
/**
* @ORM\ManyToOne(targetEntity="App\Entity\CategoryLevel1")
*/
private $categoryLevel1Id;
解决问题