Добавить ModelTransformer в динамически создаваемое поле формы

У меня есть тип формы с динамически созданным полем формы с использованием 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 с динамически созданным полем?


person Frank Mueller    schedule 26.01.2018    source источник


Ответы (1)


Анонимная функция, привязанная к событию PRE_SET_DATA, вызывается после:

$builder->get('price')

В этом причина бага, но внутри анонимной функции мы тоже не можем добавить преобразователь модели, потому что конфиг $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);
    }
});

Этот кажется более элегантным и интуитивно понятным.

person yceruto    schedule 26.01.2018