Подтвердить что ты не робот

Symfony2: Как получить ошибки проверки формы после привязки запроса к форме

Здесь мой код 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?

4b9b3361

Ответ 1

У вас есть два возможных способа сделать это:

  • не перенаправлять пользователя при ошибке и отображать {{ form_errors(form) }} в файле шаблона
  • массив ошибок доступа как $form->getErrors()

Ответ 2

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);

Docs:
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)

Ответ 3

Ниже приводится решение, которое сработало для меня. Эта функция находится в контроллере и возвращает структурированный массив всех сообщений об ошибках и поля, которое их вызвало.

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;
}

Ответ 4

Используйте 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:

Ответ 5

Чтобы получить правильные (переводимые) сообщения, в настоящее время использующие 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, если установлено значение false.

Ответ 6

Функция для 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

Для моих флеш-сообщений я был доволен $form->getErrorsAsString()

Изменить (от Benji_X80): Для использования SF3 $form->getErrors(true, false);

Ответ 8

Сообщения об ошибках переведенных форм (Symfony2.1)

Я много искал, чтобы найти эту информацию, поэтому считаю, что определенно стоит добавить заметку о переводе ошибок формы.

@Icode4food ответ вернет все ошибки формы. Однако возвращаемый массив не учитывает либо плюрализацию сообщений, либо перевод.

Вы можете изменить цикл foreach ответа @Icode4food, чтобы иметь комбо:

  • Получить все ошибки определенной формы
  • Возвращает переведенную ошибку.
  • При необходимости учитывайте плюрализацию

Вот он:

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 разных сообщений:

Ответ 9

Вы также можете использовать службу валидатора для получения нарушений ограничений:

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

Ответ 10

Сообщения об ошибках переведенной формы (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();
        }
    }

Ответ 11

Если вы используете пользовательские валидаторы, Symfony не возвращает ошибки, сгенерированные этими валидаторами в $form->getErrors(). $form->getErrorsAsString() вернет все необходимые вам ошибки, но его выход, к сожалению, форматируется как строка, а не массив.

Метод, который вы используете для получения всех ошибок (независимо от того, откуда они пришли), зависит от того, какую версию Symfony вы используете.

Большинство предлагаемых решений включают создание рекурсивной функции, которая сканирует все дочерние формы и извлекает соответствующие ошибки в один массив. Symfony 2.3 не имеет функции $form->hasChildren(), но имеет $form->all().

Вот вспомогательный класс для Symfony 2.3, который вы можете использовать для извлечения всех ошибок из любой формы. (Он основан на коде из комментария yapro по связанному биту в учетной записи gifub Symfony.)

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.
        }
    }

}

Ответ 12

Основываясь на ответе @Jay Seth, я сделал версию класса FormErrors, особенно для Ajax Forms:

// 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"
    }]
}

Объект ошибки содержит поле "ключ", которое является идентификатором входного элемента DOM, поэтому вы можете легко заполнять сообщения об ошибках.

Если у вас есть дочерние формы внутри родителя, не забудьте добавить параметр cascade_validation в родительскую форму setDefaults.

Ответ 13

СИМФОНИЯ 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."
}

Ответ 14

Для 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);
                }
            }
        }
    }

}

Ответ 15

$form- > getErrors() работает для меня.

Ответ 16

Я придумал это решение. Он отлично работает с последним Symfony 2.4.

Я попытаюсь дать некоторые объяснения.

Использование отдельного валидатора

Я думаю, что плохая идея использовать отдельную проверку для проверки сущностей и сообщений о нарушении ограничений, как это было предложено другими авторами.

  • Вам нужно будет вручную проверить все сущности, указать группы проверки и т.д. и т.д. Со сложными иерархическими формами это практически не практично и быстро выйдет из рук.

  • Таким образом, вы будете дважды проверять форму: один раз с формой и один раз с отдельным валидатором. Это плохая идея с точки зрения производительности.

Я предлагаю рекурсивно перебирать тип формы, чтобы дети собирали сообщения об ошибках.

Использование некоторых предложенных методов с эксклюзивным выражением 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;
}

Я надеюсь, что это поможет кому-то.

Ответ 17

Для 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;
}

Ответ 18

СИМФОНИЯ 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;
}

В надежде помочь