我有一个表单类型,其中包含动态创建的表单字段,使用FormEvents::PRE_SET_DATA
,如Symfony文档中所述:
https://symfony.com/doc/current/form/dynamic_form_modification.html
这很好用!
我希望在这个字段上有ModelTransformer
,因为我必须转换底层实体的表单输入。这在这里描述:
https://symfony.com/doc/current/form/data_transformers.html
这也非常好 - 独立。但不能与表格修改相结合!
这是一个例子:
<?php
namespace App\Form;
use App\Entity\Product;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\CallbackTransformer;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\Form\FormEvent;
use Symfony\Component\Form\FormEvents;
use Symfony\Component\OptionsResolver\OptionsResolver;
class ProductType extends AbstractType {
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('name');
$builder->addEventListener(FormEvents::PRE_SET_DATA, function (FormEvent $event) {
$product = $event->getData();
$form = $event->getForm();
if (!$product || null === $product->getId()) {
$form->add('price');
}
});
$builder->get('price')
->addModelTransformer(new CallbackTransformer(
function ($value) {
// ToDo: Do transformation here
return $value;
},
function ($value) {
// ToDo: Do transformation here
return $value;
}
))
;
}
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults(array(
'data_class' => Product::class
));
}
}
如果我创建并呈现此表单,我会得到:
The child with the name "price" does not exist.
这在这一点上听起来合乎逻辑 - 但有没有办法将ModelTransformer
与动态创建的字段结合起来?
答案 0 :(得分:0)
绑定到PRE_SET_DATA
事件的匿名函数在以下后调用:
$builder->get('price')
这就是bug的原因,但在匿名函数中我们也无法添加模型转换器,因为此时已经处理了$builder
配置。
作为解决方法,您可以先更改添加price
字段的逻辑,然后配置转换器并在PRE_SET_DATA
事件需要时将其删除:
$builder
->add('name')
->add('price')
;
$builder->get('price')->addModelTransformer(...);
$builder->addEventListener(FormEvents::PRE_SET_DATA, function (FormEvent $event) {
$product = $event->getData();
$form = $event->getForm();
// Inverting the condition
if ($product && null !== $product->getId()) {
$form->remove('price');
}
});
另一种解决方法是创建自己的PriceType
在那里添加模型转换器:
class PriceType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder->addModelTransformer(...);
}
//...
}
稍后,您将使用与之前相同的逻辑,但设置新的类型:
$builder->add('name');
$builder->addEventListener(FormEvents::PRE_SET_DATA, function (FormEvent $event) {
$product = $event->getData();
$form = $event->getForm();
if (!$product || null === $product->getId()) {
$form->add('price', PriceType::class);
}
});
这个看起来更优雅和直观。