dqyin0101 2013-04-23 11:54
浏览 33
已采纳

在EventListener中添加表单错误

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.'));
        }

    });

}
  • 写回答

1条回答 默认 最新

  • doulan7166 2013-05-06 08:53
    关注

    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.

    本回答被题主选为最佳回答 , 对您是否有帮助呢?
    评论

报告相同问题?

悬赏问题

  • ¥15 gdf格式的脑电数据如何处理matlab
  • ¥20 重新写的代码替换了之后运行hbuliderx就这样了
  • ¥100 监控抖音用户作品更新可以微信公众号提醒
  • ¥15 UE5 如何可以不渲染HDRIBackdrop背景
  • ¥70 2048小游戏毕设项目
  • ¥20 mysql架构,按照姓名分表
  • ¥15 MATLAB实现区间[a,b]上的Gauss-Legendre积分
  • ¥15 Macbookpro 连接热点正常上网,连接不了Wi-Fi。
  • ¥15 delphi webbrowser组件网页下拉菜单自动选择问题
  • ¥15 linux驱动,linux应用,多线程