将ModelTransformer添加到动态创建的表单字段

时间:2018-01-26 10:59:03

标签: symfony symfony-forms symfony-3.4

我有一个表单类型,其中包含动态创建的表单字段,使用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与动态创建的字段结合起来?

1 个答案:

答案 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);
    }
});

这个看起来更优雅和直观。