在EventListener中添加表单错误 [英] Adding a form error in an EventListener

查看:79
本文介绍了在EventListener中添加表单错误的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个基于Symfony 2.2的应用程序,该应用程序的表单具有仅基于表单中另一个字段才需要的字段.在提交表单时,我绑定了一个EventListener来捕获,因此我可以验证提交表单时是否实际上不需要'required'字段.

我注意到我无法在PRE_BIND表单事件中设置FormError.这样做不会显示错误,但是如果我绑定到BIND事件侦听器,则可以正确显示表单错误,但是我不想等到BIND事件检查我的错误(我不想坏数据绑定到我的实体的可能性).

有人可以告诉我为什么吗?

 public function buildForm(FormBuilderInterface $builder, array $options)
{
    // snip ...

    $builder->addEventListener(FormEvents::PRE_BIND, function(FormEvent $event) use ($options) {
        $data = $event->getData();
        $form = $event->getForm();
        if ($data === null) {
            return;
        }

        // yes, this is definitely called; If I remove the if() and just
        // and just add the formError it still doesn't work.
        if ($data['type'] == 'port' and empty($data['protocol'])) {
            $form->get('protocol')->addError(new FormError('A valid protocol must be selected.'));
        }

    });

}
 

解决方案

在这种情况下,您应该基于提交的数据使用验证组.从symfony 2.1开始可用此方法.

您不需要拉事件.看这里:

forms- http://symfony. com/doc/current/book/forms.html#groups-based-on-submitted-data

验证- http://symfony.com/doc/current/book/validation.html#validation-groups

尝试这种方法.而且您应该获得如下代码:

带有验证程序的实体脚本: src/Acme/AcmeBundle/Entity/Url.php

use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\Validator\Constraints as Assert;

...
    /**
     * @ORM\Column(name="port", type="integer")
     * @Assert\NotBlank(groups={"validation_partial", "validation_full"})
     */
    private $port;

    /**
     * @ORM\Column(name="protocol", type="string", length=10)
     * @Assert\NotBlank(groups={"validation_full"})
     */
    private $protocol;
...

表单脚本:src/Acme/AcmeBundle/Form/UrlType.php

use Symfony\Component\Form\FormInterface;
use Symfony\Component\OptionsResolver\OptionsResolverInterface;

...

public function setDefaultOptions(OptionsResolverInterface $resolver)
{
    $resolver->setDefaults(array(
        'validation_groups' => function(FormInterface $form) {
            $data = $form->getData();

            if ('port' == $data->getType()) {
                return array('validation_full');
            } else {
                return array('validation_partial');
            }
        },
    ));

 }


好的,我会尝试详细回答您的问题. 例如.我们有这样的FormType:

public function buildForm(FormBuilderInterface $builder, array $options)
{
    $builder
        ->add('name') // Some text field 'Name'
    ;

    $builder->addEventListener(FormEvents::PRE_BIND, function(FormEvent $event) use ($options) {
        $event->getForm()->get('name')->addError(new FormError('*** ERROR PRE_BIND'));
    });
    $builder->addEventListener(FormEvents::BIND, function(FormEvent $event) use ($options) {
        $event->getForm()->get('name')->addError(new FormError('*** ERROR BIND'));
    });
    $builder->addEventListener(FormEvents::POST_BIND, function(FormEvent $event) use ($options) {
        $event->getForm()->get('name')->addError(new FormError('*** ERROR POST_BIND'));
    });
}

你是对的.如果在事件的侦听器中添加错误:PRE_BIND,BIND,POST_BIND. 您将仅从BIND和POST_BIND事件中获得错误.要了解为什么会这样,您需要知道2点.

第一件事: 形式中的每个元素也是形式.在我们的例子中,我们的主要表单有子级"Name"(文本元素),这也是一个表单.

[MainForm]

-> [NameForm]

//如果您的表单包含其他元素,则可以有其他表单

第二件事: 当您将请求绑定到MainForm时,您将调用bind()函数.

此函数为MainForm的每个子级调用bind()函数.

该函数的算法中有问题的答案. bind()函数算法:

function bind($submittedData) {
    1) clear all errors
    2) dispatch event PRE_BIND
    3) invoke bind() function for children
    4) dispatch event BIND
    5) dispatch event POST_BIND
}

因此,根据我们的示例程序流程,将是:

Invoke bind() function for MainForm
1) MainForm - clear all errors
2) MainForm - dispatch event PRE_BIND // there our listener add error for child NameForm.
3) MainForm - invoke bind() function for child NameForm:
    1) NameForm - clear all errors // is the answer for your question, error from event MainForm:PRE_BIND cleared!!!
    2) NameForm - dispatch event PRE_BIND // no changes
    3) NameForm - invoke bind() for children // this form does not have children, so will be passed
    4) NameForm - dispatch event BIND // no changes
    5) NameForm - dispatch event POST_BIND // no changes
4) MainForm - dispatch event BIND // there our listener add error to child NameForm
5) MainForm - dispatch event POST_BIND // there our listener add another error to child NameForm.

我希望这个解释对您有所帮助.

I have a Symfony 2.2 based application with a form that has a field that is only required based on another field in the form. I bound an EventListener to catch when the form is submitted so I can verify if the 'required' field is actually not needed when the form is submitted.

I've noticed that I can't set a FormError inside the PRE_BIND form event. Doing so doesn't show the error, but if I bind to the BIND event listener then the form error is displayed properly but I don't want to wait until the BIND event to check for my errors (I don't want the potential of bad data being bound to my entity).

Can someone tell me why this is so?

public function buildForm(FormBuilderInterface $builder, array $options)
{
    // snip ...

    $builder->addEventListener(FormEvents::PRE_BIND, function(FormEvent $event) use ($options) {
        $data = $event->getData();
        $form = $event->getForm();
        if ($data === null) {
            return;
        }

        // yes, this is definitely called; If I remove the if() and just
        // and just add the formError it still doesn't work.
        if ($data['type'] == 'port' and empty($data['protocol'])) {
            $form->get('protocol')->addError(new FormError('A valid protocol must be selected.'));
        }

    });

}

解决方案

In this case you should use Validation Groups based on Submited Data. This method available since symfony 2.1.

And you don't need to pull events. Look here:

forms - http://symfony.com/doc/current/book/forms.html#groups-based-on-submitted-data

validation - http://symfony.com/doc/current/book/validation.html#validation-groups

Try this approach. And you should get code like this:

Entity script with validators: src/Acme/AcmeBundle/Entity/Url.php

use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\Validator\Constraints as Assert;

...
    /**
     * @ORM\Column(name="port", type="integer")
     * @Assert\NotBlank(groups={"validation_partial", "validation_full"})
     */
    private $port;

    /**
     * @ORM\Column(name="protocol", type="string", length=10)
     * @Assert\NotBlank(groups={"validation_full"})
     */
    private $protocol;
...

Form script: src/Acme/AcmeBundle/Form/UrlType.php

use Symfony\Component\Form\FormInterface;
use Symfony\Component\OptionsResolver\OptionsResolverInterface;

...

public function setDefaultOptions(OptionsResolverInterface $resolver)
{
    $resolver->setDefaults(array(
        'validation_groups' => function(FormInterface $form) {
            $data = $form->getData();

            if ('port' == $data->getType()) {
                return array('validation_full');
            } else {
                return array('validation_partial');
            }
        },
    ));

 }


Ok, I will try answer on your question in detail. For example. We have FormType like this:

public function buildForm(FormBuilderInterface $builder, array $options)
{
    $builder
        ->add('name') // Some text field 'Name'
    ;

    $builder->addEventListener(FormEvents::PRE_BIND, function(FormEvent $event) use ($options) {
        $event->getForm()->get('name')->addError(new FormError('*** ERROR PRE_BIND'));
    });
    $builder->addEventListener(FormEvents::BIND, function(FormEvent $event) use ($options) {
        $event->getForm()->get('name')->addError(new FormError('*** ERROR BIND'));
    });
    $builder->addEventListener(FormEvents::POST_BIND, function(FormEvent $event) use ($options) {
        $event->getForm()->get('name')->addError(new FormError('*** ERROR POST_BIND'));
    });
}

You are right. If add errors in listeners for events: PRE_BIND, BIND, POST_BIND. You will get only errors from BIND and POST_BIND events. To understand why this is so you need to know 2 points.

First thing: Every element in form is also form. In our case our main form has children 'Name'(text element) which is also a form.

[MainForm]

-> [NameForm]

// there can be additional forms if your form has another elements

Second thing: When you bind request to a MainForm you invoke bind() function.

And this function invoke bind() function for every child of MainForm.

Answer for your question is in algorithm of this function. bind() function algorithm:

function bind($submittedData) {
    1) clear all errors
    2) dispatch event PRE_BIND
    3) invoke bind() function for children
    4) dispatch event BIND
    5) dispatch event POST_BIND
}

So based of our example programm flow will be:

Invoke bind() function for MainForm
1) MainForm - clear all errors
2) MainForm - dispatch event PRE_BIND // there our listener add error for child NameForm.
3) MainForm - invoke bind() function for child NameForm:
    1) NameForm - clear all errors // is the answer for your question, error from event MainForm:PRE_BIND cleared!!!
    2) NameForm - dispatch event PRE_BIND // no changes
    3) NameForm - invoke bind() for children // this form does not have children, so will be passed
    4) NameForm - dispatch event BIND // no changes
    5) NameForm - dispatch event POST_BIND // no changes
4) MainForm - dispatch event BIND // there our listener add error to child NameForm
5) MainForm - dispatch event POST_BIND // there our listener add another error to child NameForm.

I hope this explanation is helpful for you.

这篇关于在EventListener中添加表单错误的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆