Symfony2: как получить ошибки проверки формы после привязки запроса к форме - PullRequest
108 голосов
/ 08 августа 2011

Вот мой 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?

Ответы [ 19 ]

2 голосов
/ 07 апреля 2016

Основываясь на ответе @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.

2 голосов
/ 14 марта 2014

Если вы используете пользовательские валидаторы, 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.
        }
    }

}
2 голосов
/ 07 февраля 2017

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."
}
1 голос
/ 28 апреля 2014

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

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

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

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

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

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

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

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

Надеюсь, это кому-нибудь поможет.

1 голос
/ 28 октября 2013

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

1 голос
/ 02 декабря 2016

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

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

1 голос
/ 24 марта 2018

Для 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 , если хотите избавиться от надоедливого текста ' Error: ' в каждом тексте описания ошибки.

$errors[$child->getName()] = str_replace('ERROR:', '', (string) $child->getErrors(true, false));
1 голос
/ 09 июля 2013

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

}
0 голосов
/ 22 мая 2013

Для 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;
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...