如何在事件监听器中添加数据转换器

问题描述 投票:0回答:3

所以,这是我的问题:我必须根据其基础数据向表单添加一个字段,但我必须向该字段添加一个数据转换器。

我认为解决方案很简单,只需向表单添加一个 PRE_SET_DATA 事件监听器(只是为了访问底层数据)并在监听器内添加字段和转换器。但我无法在侦听器内添加变压器,因为表单已经被锁定。

我尝试了很多解决方法,但无法解决。她是我的代码:

$builder->...
    ->add(
        $builder->create('date', 'choice', array(
            'label' => 'form.game.date',
            'empty_value' => 'form.game.date',
            'required' => false,
            'choices' => array(
                '2014-04-10' => '10/Apr', // just to test
                '2014-04-11' => '11/Apr',
                )
            ))
            ->addModelTransformer(new DateToStringTransformer())
        );

$builder->addEventListener(FormEvents::PRE_SET_DATA, function (FormEvent $event) use ($builder) {
    $game = $event->getData();
    $form = $event->getForm();

    $period = new \DatePeriod(
        $game->getTournament()->getBeginDate(),
        new \DateInterval('P1D'),
        $game->getTournament()->getEndDate()->add(new \DateInterval('P1D'))
    );

    $dates = array();

    foreach($period as $date){
        $dates[$date->format("Y-m-d")] = $date->format("j/M");
    }

    $form->add('date', 'choice', array(
        'label' => 'form.game.date',
        'choices' => $dates,
        ));
});

当我将日期字段添加到事件侦听器内的表单时,之前添加的数据字段将被替换,因此它是数据转换器...

有办法吗?

forms symfony symfony-forms
3个回答
1
投票

我写了一些测试并更新了你的代码。检查我是否正确理解你的问题。

SomeTypeTest.php:

<?php
class SomeTypeTest extends TypeTestCase
{
    /**
     * @test
     */
    public function testSubmitValidData()
    {
        $begin = new \DateTime();
        $formData = array(
            'date' => '2014-01-15'
        );

        $type = new SomeType();
        $form = $this->factory->create($type);

        $form->submit($formData);

        $this->assertTrue($form->isSynchronized());
        $this->assertEquals(['date' => \DateTime::createFromFormat('Y-m-d', '2014-01-15')], $form->getData());

        $view = $form->createView();
        $children = $view->children;

        foreach (array_keys($formData) as $key) {
            $this->assertArrayHasKey($key, $children);
        }
    }
}

SomeType.php:

<?php
class SomeType extends AbstractType
{
    public function buildForm(FormBuilderInterface $builder, array $options)
    {
        $builder
            ->addEventListener(FormEvents::PRE_SET_DATA, function (FormEvent $event) use ($builder) {
                //$game = $event->getData();
                $form = $event->getForm();

                $period = new \DatePeriod(
                    \DateTime::createFromFormat('Y-m-d', '2014-01-01'), // for test
                    new \DateInterval('P1D'),
                    \DateTime::createFromFormat('Y-m-d', '2014-01-30') // for test
                );

                $dates = array();

                foreach ($period as $date) {
                    $dates[$date->format("Y-m-d")] = $date->format("j/M");
                }

                $form->add($builder->create('date', 'choice', array(
                        'label' => 'form.game.date',
                        'empty_value' => 'form.game.date',
                        'auto_initialize' => false,
                        'required' => false,
                        'choices' => $dates
                    ))
                    ->addModelTransformer(new DateToStringTransformer())->getForm()
                );
            });
    }

    public function getName()
    {
        return 'st';
    }
}

DateToStringTransformer.php:

<?php
class DateToStringTransformer implements DataTransformerInterface
{
    /**
     * @param mixed $value
     * @return mixed|void
     */
    public function transform($value)
    {
        if (!$value) {
            return null;
        }

        return $value->format('Y-m-d');
    }

    /**
     * @param mixed $value
     * @return mixed|void
     */
    public function reverseTransform($value)
    {
        return \DateTime::createFromFormat('Y-m-d', $value);
    }
}

https://gist.github.com/ChubV/11348928


1
投票

我成功地通过创建始终添加数据转换器的自定义类型来使其工作。然后我可以从任何事件监听器调用“form->add('date', 'my_type',..)”,而不会丢失数据转换器。

MyType.php

class MyType extends AbstractType
{
    public function buildForm(FormBuilderInterface $builder, array $options)
    {
        $builder
            ->add('field1')
            ->add('field2')
            ...;

        $builder->addEventSubscriber(new AddDateSubscriber());
    }
}

自定义类型.php

class DateChoiceType extends AbstractType
{
    public function buildForm(FormBuilderInterface $builder, array $options)
    {
        $builder->addModelTransformer(new DateToStringTransformer());
    }

    public function setDefaultOptions(OptionsResolverInterface $resolver)
    {
        $resolver->setDefaults(array(
            'invalid_message' => 'The selected date does not exist',
        ));
    }

    public function getParent()
    {
        return 'choice';
    }

    public function getName()
    {
        return 'date_choice';
    }
}

每次我向表单添加 date_choice 类型时,数据转换器也会被添加。

class AddDateSubscriber implements EventSubscriberInterface
{
    public static function getSubscribedEvents()
    {
        return array(FormEvents::PRE_SET_DATA => 'preSetData');
    }

    public function preSetData(FormEvent $event)
    {
        $game = $event->getData();
        $form = $event->getForm();

        $endDate = \DateTime::createFromFormat('Y-m-d', $game->getTournament()->getEndDate()->format('Y-m-d'));

        $period = new \DatePeriod(
            $game->getTournament()->getBeginDate(),
            new \DateInterval('P1D'),
            $endDate
        );

        $dates = array();
        foreach($period as $date){
            $dates[$date->format("Y-m-d")] = $date->format("j/M");
        }

        $form->add('date', 'date_choice', array(
            'label' => 'form.game.date.label',
            'empty_value' => 'form.game.date.none',
            'required' => false,
            'choices' => $dates,
            ));
    }
}

DateToStringTransformer.php

class DateToStringTransformer implements DataTransformerInterface
{
    public function transform($date)
    {
        if (null === $date) {

            return "";
        }

        return $date->format("Y-m-d");
    }

    public function reverseTransform($stringDate)
    {
        if (!$stringDate) {

            return null;
        }

        $date = \DateTime::createFromFormat('Y-m-d', $stringDate);

        if (false === $date) {

            throw new TransformationFailedException('Sting to date transformation failed!');
        }

        return $date;
    }
}

希望这会对某人有所帮助。


0
投票

真实案例:我有一个表单类型,其中有一个选择列表,其中包含描述 HTML 输入类型(文本、文本区域、选择等...)的预填充值

选择 select 选项后,应显示名为 possibleOptions 的第二个字段。

此外,我有一个 transformer 应该应用于第二个字段,它将输入标签值转换为数组,反之亦然。

默认情况下,当我执行 creation 时,第二个字段将被隐藏,直到选择 select 选项。但是,在 edit 操作中,默认情况下应显示第二个字段,因为我选择了 select 选项。

我使用事件监听器 PRE_SET_DATA 来自定义渲染。

这是我的完整解决方案:

public function buildForm(FormBuilderInterface $builder, array $options): void
{
    $builder
        ->add('type', ChoiceType::class, [
            "label" => "Type du champs",
            "choices" => [
                'Texte' => FieldTypeEnum::TEXT->value,
                'Email' => FieldTypeEnum::EMAIL->value,
                'Tél' => FieldTypeEnum::PHONE->value,
                'Lien' => FieldTypeEnum::URL->value,
                'Nombre' => FieldTypeEnum::NUMBER->value,
                'Date' => FieldTypeEnum::DATE->value,
                'Code secret' => FieldTypeEnum::SECRET_CODE->value,
                'Liste déroulante' => FieldTypeEnum::SELECT->value,
                'Text Long' => FieldTypeEnum::TEXTAREA->value,
            ]
        ])
        ->add('possibleOptions', TextType::class, [
            'label' => 'Choix possibles',
            'attr' => [
                'data-tags' => true
            ],
            'row_attr' => [
                'class' => 'd-none'
            ],
            'required' => true
        ])
       ;

    $builder->get('possibleOptions')->addModelTransformer($this->stringToArrayTransformer);

    $builder->addEventListener(FormEvents::PRE_SET_DATA, function (FormEvent $event) use ($builder) {
        if ($event->getData() instanceof Field) {
            /**
             * @var Field $field
             */
            $field = $event->getData();
            $form = $event->getForm();

            if ($field->getType() === FieldTypeEnum::SELECT->value && $field->getId()) {
                $form->add(
                    $builder->create(
                        'possibleOptions', TextType::class, [
                                             'label' => 'Choix possibles',
                                             'attr' => [
                                                 'data-tags' => true
                                             ],
                                             'required' => true,
                                             'auto_initialize' => false,
                                         ]
                    )->addModelTransformer($this->stringToArrayTransformer)->getForm()
                );
            }
        }
    });
}

希望这能有所帮助!

© www.soinside.com 2019 - 2024. All rights reserved.