3

我有一个使用FormEvents::PRE_SET_DATASymfony 文档中描述的动态创建表单字段的表单类型: 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与动态创建的字段结合起来?

4

1 回答 1

1

绑定到事件的匿名函数在以下情况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);
    }
});

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

于 2018-01-26T14:50:19.493 回答