Symfony2:如何在将请求绑定到表单后获取表单验证错误

时间:2011-08-08 06:58:58

标签: symfony

这是我的saveAction代码(表单将数据传递给的地方)

public function saveAction()
{
    $user = OBUser();

    $form = $this->createForm(new OBUserType(), $user);

    if ($this->request->getMethod() == 'POST')
    {
        $form->bindRequest($this->request);
        if ($form->isValid())
            return $this->redirect($this->generateUrl('success_page'));
        else
            return $this->redirect($this->generateUrl('registration_form'));
    } else
        return new Response();
}

我的问题是:如果$form->isValid()返回false,我该如何解决错误?

21 个答案:

答案 0 :(得分:116)

您有两种可能的方法:

  • 错误时不会重定向用户并在模板文件
  • 中显示{{ form_errors(form) }}
  • 将错误数组访问为$form->getErrors()

答案 1 :(得分:98)

Symfony 2.3 / 2.4:

这个函数得到了所有的错误。表单上的“CSRF令牌无效。请尝试重新提交表单。”以及没有错误冒泡的表单子上的其他错误。

private function getErrorMessages(\Symfony\Component\Form\Form $form) {
    $errors = array();

    foreach ($form->getErrors() as $key => $error) {
        if ($form->isRoot()) {
            $errors['#'][] = $error->getMessage();
        } else {
            $errors[] = $error->getMessage();
        }
    }

    foreach ($form->all() as $child) {
        if (!$child->isValid()) {
            $errors[$child->getName()] = $this->getErrorMessages($child);
        }
    }

    return $errors;
}

以字符串形式获取所有错误:

$string = var_export($this->getErrorMessages($form), true);

Symfony 2.5 / 3.0:

$string = (string) $form->getErrors(true, false);

文档:
https://github.com/symfony/symfony/blob/master/UPGRADE-2.5.md#form https://github.com/symfony/symfony/blob/master/UPGRADE-3.0.md#form(在底部:The method Form::getErrorsAsString() was removed

答案 2 :(得分:47)

以下是适合我的解决方案。此函数位于控制器中,将返回所有错误消息和导致它们的字段的结构化数组。

Symfony 2.0:

private function getErrorMessages(\Symfony\Component\Form\Form $form) {
    $errors = array();
    foreach ($form->getErrors() as $key => $error) {
        $template = $error->getMessageTemplate();
        $parameters = $error->getMessageParameters();

        foreach($parameters as $var => $value){
            $template = str_replace($var, $value, $template);
        }

        $errors[$key] = $template;
    }
    if ($form->hasChildren()) {
        foreach ($form->getChildren() as $child) {
            if (!$child->isValid()) {
                $errors[$child->getName()] = $this->getErrorMessages($child);
            }
        }
    }

    return $errors;
}

Symfony 2.1及更新版本:

private function getErrorMessages(\Symfony\Component\Form\Form $form) {      
    $errors = array();

    if ($form->hasChildren()) {
        foreach ($form->getChildren() as $child) {
            if (!$child->isValid()) {
                $errors[$child->getName()] = $this->getErrorMessages($child);
            }
        }
    } else {
        foreach ($form->getErrors() as $key => $error) {
            $errors[] = $error->getMessage();
        }   
    }

    return $errors;
}

答案 3 :(得分:34)

使用Validator获取特定实体的错误

if( $form->isValid() )
{
    // ...
}
else
{
    // get a ConstraintViolationList
    $errors = $this->get('validator')->validate( $user );

    $result = '';

    // iterate on it
    foreach( $errors as $error )
    {
        // Do stuff with:
        //   $error->getPropertyPath() : the field that caused the error
        //   $error->getMessage() : the error message
    }
}

API参考:

答案 4 :(得分:19)

为了获得正确的(可翻译的)消息,目前正在使用SF 2.6.3,这是我的最终功能(因为以上任何一个似乎都不再起作用):

 private function getErrorMessages(\Symfony\Component\Form\Form $form) {      
    $errors = array();
    foreach ($form->getErrors(true, false) as $error) {
        // My personnal need was to get translatable messages
        // $errors[] = $this->trans($error->current()->getMessage());
        $errors[] = $error->current()->getMessage();
    }

    return $errors;
}

因为Form :: getErrors()方法现在返回 FormErrorIterator 的实例,除非您将第二个参数($ flatten)切换为 true 。 (然后它将返回一个 FormError 实例,你必须直接调用getMessage()方法,而不使用current()方法:

 private function getErrorMessages(\Symfony\Component\Form\Form $form) {      
    $errors = array();
    foreach ($form->getErrors(true, true) as $error) {
        // My personnal need was to get translatable messages
        // $errors[] = $this->trans($error->getMessage());
        $errors[] = $error->getMessage();
    }

    return $errors;
}

最重要的是将第一个参数设置为true以获取错误。 将第二个参数($ flatten)保留为其默认值( true )将返回 FormError 实例,而当设置为时,将返回 FormErrorIterator 实例假的。

答案 5 :(得分:15)

对于我的Flash消息,我对$form->getErrorsAsString()

感到满意

编辑(来自Benji_X80): 对于SF3,请使用$form->getErrors(true, false);

答案 6 :(得分:15)

symfony 2.1及更新版本的功能,没有任何弃用功能:

/**
 * @param \Symfony\Component\Form\Form $form
 *
 * @return array
 */
private function getErrorMessages(\Symfony\Component\Form\Form $form)
{
    $errors = array();

    if ($form->count() > 0) {
        foreach ($form->all() as $child) {
            /**
             * @var \Symfony\Component\Form\Form $child
             */
            if (!$child->isValid()) {
                $errors[$child->getName()] = $this->getErrorMessages($child);
            }
        }
    } else {
        /**
         * @var \Symfony\Component\Form\FormError $error
         */
        foreach ($form->getErrors() as $key => $error) {
            $errors[] = $error->getMessage();
        }
    }

    return $errors;
}

答案 7 :(得分:4)

翻译的表单错误消息(Symfony2.1)

我一直在努力寻找这些信息,所以我认为绝对值得添加关于表单错误翻译的说明。

@Icode4food answer将返回表单的所有错误。但是,返回的数组不会考虑消息多元化翻译

您可以修改@Icode4food answer的foreach循环以获得组合:

  • 获取特定表单的所有错误
  • 返回已翻译的错误
  • 如有必要,请考虑多元化

这是:

foreach ($form->getErrors() as $key => $error) {

   //If the message requires pluralization
    if($error->getMessagePluralization() !== null) {
        $errors[] = $this->container->get('translator')->transChoice(
            $error->getMessage(), 
            $error->getMessagePluralization(), 
            $error->getMessageParameters(), 
            'validators'
            );
    } 
    //Otherwise, we do a classic translation
    else {
        $errors[] = $this->container->get('translator')->trans(
            $error->getMessage(), 
            array(), 
            'validators'
            );
    }
}

这个答案来自3个不同的帖子:

答案 8 :(得分:3)

您还可以使用验证程序服务来获取约束违规行为:

$errors = $this->get('validator')->validate($user);

答案 9 :(得分:3)

翻译的表单错误消息(Symfony2.3)

我解决问题的版本:

/src/Acme/MyBundle/Resources/config/services.yml

services:
    form_errors:
        class: Acme\MyBundle\Form\FormErrors

/src/Acme/MyBundle/Form/FormErrors.php

<?php
namespace Acme\MyBundle\Form;

class FormErrors
{
    public function getArray(\Symfony\Component\Form\Form $form)
    {
        return $this->getErrors($form);
    }

    private function getErrors($form)
    {
        $errors = array();

        if ($form instanceof \Symfony\Component\Form\Form) {

            // соберем ошибки элемента
            foreach ($form->getErrors() as $error) {

                $errors[] = $error->getMessage();
            }

            // пробежимся под дочерним элементам
            foreach ($form->all() as $key => $child) {
                /** @var $child \Symfony\Component\Form\Form */
                if ($err = $this->getErrors($child)) {
                    $errors[$key] = $err;
                }
            }
        }

        return $errors;
    }
}

/src/Acme/MyBundle/Controller/DefaultController.php

$form = $this->createFormBuilder($entity)->getForm();
$form_errors = $this->get('form_errors')->getArray($form);
return new JsonResponse($form_errors);

在Symfony 2.5 中,您可以非常轻松地获取所有字段错误:

    $errors = array();
    foreach ($form as $fieldName => $formField) {
        foreach ($formField->getErrors(true) as $error) {
            $errors[$fieldName] = $error->getMessage();
        }
    }

答案 10 :(得分:2)

对于 Symfony 5 我得到了这个:

public function getErrorMessages(Form $form) {
  
    $errors = [];        

    foreach ($form->all() as $child) {
        foreach ($child->getErrors() as $error) {
                $name = $child->getName();
                $errors[$name] = $error->getMessage();
        }
    }

    return $errors;
}

我得到了这种 JSON 的东西:

errors: {
    firstname: "The firstname must be between 2 and 30 characters long.",
    lastname: "The lastname must be between 2 and 40 characters long."
    subject: "Please choose a subject."
    text: "Empty messages are not allowed."
}

这对我来说已经足够好用了,但我想它不会在全局层面发现错误,比如超时令牌或其他类似的东西。 无论如何,我一直在寻找适合我的情况的解决方案很长一段时间,我很高兴在这里做出贡献,希望它可以在某个时候以某种方式帮助某些开发人员。

答案 11 :(得分:2)

SYMFONY 3.X

这里给出的其他SF 3.X方法对我不起作用,因为我可以向表单提交空数据(但我有NotNull / NotBlanck约束)。在这种情况下,错误字符串将如下所示:

string(282) "ERROR: This value should not be blank.
ERROR: This value should not be blank.
ERROR: This value should not be blank.
ERROR: This value should not be blank.
ERROR: This value should not be blank.
ERROR: This value should not be null.
name:
    ERROR: This value should not be blank.
"

哪个不是很有用。所以我做到了:

public function buildErrorArray(FormInterface $form)
{
    $errors = [];

    foreach ($form->all() as $child) {
        $errors = array_merge(
            $errors,
            $this->buildErrorArray($child)
        );
    }

    foreach ($form->getErrors() as $error) {
        $errors[$error->getCause()->getPropertyPath()] = $error->getMessage();
    }

    return $errors;
}

哪会回复:

array(7) {
  ["data.name"]=>
  string(31) "This value should not be blank."
  ["data.street"]=>
  string(31) "This value should not be blank."
  ["data.zipCode"]=>
  string(31) "This value should not be blank."
  ["data.city"]=>
  string(31) "This value should not be blank."
  ["data.state"]=>
  string(31) "This value should not be blank."
  ["data.countryCode"]=>
  string(31) "This value should not be blank."
  ["data.organization"]=>
  string(30) "This value should not be null."
}

答案 12 :(得分:2)

根据@Jay Seth的回答,我特别为Ajax Forms创建了一个FormErrors类的版本:

// src/AppBundle/Form/FormErrors.php
namespace AppBundle\Form;

class FormErrors
{

    /**
     * @param \Symfony\Component\Form\Form $form
     *
     * @return array $errors
     */
    public function getArray(\Symfony\Component\Form\Form $form)
    {
        return $this->getErrors($form, $form->getName());
    }

    /**
     * @param \Symfony\Component\Form\Form $baseForm
     * @param \Symfony\Component\Form\Form $baseFormName
     *
     * @return array $errors
     */
    private function getErrors($baseForm, $baseFormName) {
        $errors = array();
        if ($baseForm instanceof \Symfony\Component\Form\Form) {
            foreach($baseForm->getErrors() as $error) {
                $errors[] = array(
                    "mess"      => $error->getMessage(),
                    "key"       => $baseFormName
                );
            }

            foreach ($baseForm->all() as $key => $child) {
                if(($child instanceof \Symfony\Component\Form\Form)) {
                    $cErrors = $this->getErrors($child, $baseFormName . "_" . $child->getName());
                    $errors = array_merge($errors, $cErrors);
                }
            }
        }
        return $errors;
    }
}

用法(例如在你的行动中):

$errors = $this->get('form_errors')->getArray($form);

Symfony版本:2.8.4

示例JSON响应:

{
    "success": false,
    "errors": [{
        "mess": "error_message",
        "key": "RegistrationForm_user_firstname"
    }, {
        "mess": "error_message",
        "key": "RegistrationForm_user_lastname"
    }, {
        "mess": "error_message",
        "key": "RegistrationForm_user_email"
    }, {
        "mess": "error_message",
        "key": "RegistrationForm_user_zipCode"
    }, {
        "mess": "error_message",
        "key": "RegistrationForm_user_password_password"
    }, {
        "mess": "error_message",
        "key": "RegistrationForm_terms"
    }, {
        "mess": "error_message2",
        "key": "RegistrationForm_terms"
    }, {
        "mess": "error_message",
        "key": "RegistrationForm_marketing"
    }, {
        "mess": "error_message2",
        "key": "RegistrationForm_marketing"
    }]
}

错误对象包含&#34;键&#34; field,它是输入DOM元素的id,因此您可以轻松填充错误消息。

如果您在家长中有子表单,请不要忘记在父表单cascade_validation中添加setDefaults选项。

答案 13 :(得分:2)

如果您使用的是自定义验证程序,Symfony不会返回$form->getErrors()中这些验证程序生成的错误。 $form->getErrorsAsString()将返回您需要的所有错误,但遗憾的是它的输出格式化为字符串,而不是数组。

用于获取所有错误(无论它们来自何处)的方法取决于您使用的Symfony版本。

大多数建议的解决方案涉及创建一个扫描所有子表单的递归函数,并将相关错误提取到一个数组中。 Symfony 2.3没有$form->hasChildren()函数,但确实有$form->all()

这是Symfony 2.3的帮助程序类,您可以使用它从任何表单中提取所有错误。 (它基于yapro对Symfony的github帐户中相关错误票据的评论代码。)

namespace MyApp\FormBundle\Helpers;

use Symfony\Component\Form\Form;

class FormErrorHelper
{
    /**
     * Work-around for bug where Symfony (2.3) does not return errors from custom validaters,
     * when you call $form->getErrors().
     * Based on code submitted in a comment here by yapro:
     * https://github.com/symfony/symfony/issues/7205
     *
     * @param Form $form
     * @return array Associative array of all errors
     */
    public function getFormErrors($form)
    {
        $errors = array();

        if ($form instanceof Form) {
            foreach ($form->getErrors() as $error) {
                $errors[] = $error->getMessage();
            }

            foreach ($form->all() as $key => $child) {
                /** @var $child Form */
                if ($err = $this->getFormErrors($child)) {
                    $errors[$key] = $err;
                }
            }
        }

        return $errors;
    }
}

致电代码:

namespace MyApp\ABCBundle\Controller;

use MyApp\FormBundle\Helpers;

class MyController extends Controller
{
    public function XYZAction()
    {
        // Create form.

        if (!$form->isValid()) {
            $formErrorHelper = new FormErrorHelper();
            $formErrors = $formErrorHelper->getFormErrors($form);

            // Set error array into twig template here.
        }
    }

}

答案 14 :(得分:1)

我提出了这个解决方案。它与最新的 Symfony 2.4 结合使用。

我会尝试给出一些解释。

使用单独的验证器

我认为使用单独的验证来验证实体并返回约束违规消息是一个坏主意,就像其他作者所建议的那样。

  1. 您需要手动验证所有实体,指定验证组等。对于复杂的分层表单,它根本不实用,并且会很快失控。

  2. 通过这种方式,您将对表单进行两次验证:一次使用表单,一次使用单独的验证器。从绩效角度来看,这是一个糟糕的主意。

  3. 我建议用它的子节点递归迭代表单类型以收集错误消息。

    使用一些建议的方法和独占的IF语句

    其他作者提出的一些答案包含互相排斥的IF语句,如下所示:if ($form->count() > 0)if ($form->hasChildren())

    据我所知,每种形式都可能有错误和孩子。我不是 Symfony Forms 组件的专家,但实际上你不会得到表单本身的一些错误,比如 CSRF保护错误额外字段错误。我建议删除这种分离。

    使用非规范化结果结构

    有些作者建议将所有错误放在普通数组中。因此,表单本身及其子项的所有错误消息都将添加到具有不同索引策略的相同数组中:基于数字的类型自身错误和基于名称的子错误。我建议使用以下形式的规范化数据结构:

    errors:
        - "Self error"
        - "Another self error"
    
    children
        - "some_child":
            errors:
                - "Children error"
                - "Another children error"
    
            children
                - "deeper_child":
                    errors:
                        - "Children error"
                        - "Another children error"
    
        - "another_child":
            errors:
                - "Children error"
                - "Another children error"
    

    这样结果可以在以后轻松迭代。

    我的解决方案

    所以这是我解决这个问题的方法:

    use Symfony\Component\Form\Form;
    
    /**
     * @param Form $form
     * @return array
     */
    protected function getFormErrors(Form $form)
    {
        $result = [];
    
        // No need for further processing if form is valid.
        if ($form->isValid()) {
            return $result;
        }
    
        // Looking for own errors.
        $errors = $form->getErrors();
        if (count($errors)) {
            $result['errors'] = [];
            foreach ($errors as $error) {
                $result['errors'][] = $error->getMessage();
            }
        }
    
        // Looking for invalid children and collecting errors recursively.
        if ($form->count()) {
            $childErrors = [];
            foreach ($form->all() as $child) {
                if (!$child->isValid()) {
                    $childErrors[$child->getName()] = $this->getFormErrors($child);
                }
            }
            if (count($childErrors)) {
                $result['children'] = $childErrors;
            }
        }
    
        return $result;
    }
    

    我希望它能帮助别人。

答案 15 :(得分:1)

$ form-&gt; getErrors()适合我。

答案 16 :(得分:1)

SYMFONY 3.1

我只是实现了一个静态方法来处理错误显示

static function serializeFormErrors(Form\Form $form)
{
    $errors = array();
    /**
     * @var  $key
     * @var Form\Form $child
     */
    foreach ($form->all() as $key => $child) {
        if (!$child->isValid()) {
            foreach ($child->getErrors() as $error) {
                $errors[$key] = $error->getMessage();
            }
        }
    }

    return $errors;
}

希望提供帮助

答案 17 :(得分:1)

对于Symfony 2.1以及与Twig错误显示一起使用我更改了函数以添加FormError而不是简单地检索它们,这样您就可以更好地控制错误,而不必对每个单独的输入使用error_bubbling。如果您未按以下方式设置{{form_errors(form)}}将保持空白:

/**
 * @param \Symfony\Component\Form\Form $form
 *
 * @return void
 */
private function setErrorMessages(\Symfony\Component\Form\Form $form) {      

    if ($form->count() > 0) {
        foreach ($form->all() as $child) {
            if (!$child->isValid()) {
                if( isset($this->getErrorMessages($child)[0]) ) {
                    $error = new FormError( $this->getErrorMessages($child)[0] );
                    $form->addError($error);
                }
            }
        }
    }

}

答案 18 :(得分:1)

对于 Symfony 3.2 及以上,请使用此

public function buildErrorArray(FormInterface $form)
{
    $errors = array();

    foreach ($form->getErrors() as $key => $error) {
        if ($form->isRoot()) {
            $errors['#'][] = $error->getMessage();
        } else {
            $errors[] = $error->getMessage();
        }
    }

    foreach ($form->all() as $child) {
        if (!$child->isValid()) {
            $errors[$child->getName()] = (string) $child->getErrors(true, false);
        }
    }
    return $errors;
}

如果您想摆脱每个错误说明文字中恼人的“错误:”文字,请使用 str_replace

$errors[$child->getName()] = str_replace('ERROR:', '', (string) $child->getErrors(true, false));

答案 19 :(得分:1)

Symfony 3及更高版本

我最近制作了一个函数,该函数创建了一个表单错误树。这将有助于将错误列表返回到前端。这基于具有以下内容的表单类型:

'error_bubbling' => false

代码:

public static function getFormErrorsTree(FormInterface $form): array
{
    $errors = [];

    if (count($form->getErrors()) > 0) {
        foreach ($form->getErrors() as $error) {
            $errors[] = $error->getMessage();
        }
    } else {
        foreach ($form->all() as $child) {
            $childTree = self::getFormErrorsTree($child);

            if (count($childTree) > 0) {
                $errors[$child->getName()] = $childTree;
            }
        }
    }

    return $errors;
}

输出:

Array
(
    [name] => Array
        (
            [0] => This value is not valid.
        )

    [emails] => Array
        (
            [0] => Array
                (
                    [0] => Given e-mail is not valid.
                    [1] => Given e-mail is not valid #2.
                )
            [1] => Array
                (
                    [0] => Given e-mail is not valid.
                    [1] => Given e-mail is not valid #2.
                )

        )

)

通知:我知道,如果高层有错误,则可以覆盖更深层次的字段中的错误,但这是我特意使用的。

答案 20 :(得分:0)

对于Symfony 2.1:

这是我的最终解决方案,将许多其他解决方案放在一起:

protected function getAllFormErrorMessages($form)
{
    $retval = array();
    foreach ($form->getErrors() as $key => $error) {
        if($error->getMessagePluralization() !== null) {
            $retval['message'] = $this->get('translator')->transChoice(
                $error->getMessage(), 
                $error->getMessagePluralization(), 
                $error->getMessageParameters(), 
                'validators'
            );
        } else {
            $retval['message'] = $this->get('translator')->trans($error->getMessage(), array(), 'validators');
        }
    }
    foreach ($form->all() as $name => $child) {
        $errors = $this->getAllFormErrorMessages($child);
        if (!empty($errors)) {
           $retval[$name] = $errors; 
        }
    }
    return $retval;
}