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?


person putolaruan    schedule 08.08.2011    source источник


Ответы (21)


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

  • не перенаправлять пользователя при ошибке и отображать {{ form_errors(form) }} в файле шаблона
  • доступ к массиву ошибок как $form->getErrors()
person nefo_x    schedule 08.08.2011
comment
Я сделал второе, что вы предложили, но form->getErrors() возвращает пустой массив. - person putolaruan; 08.08.2011
comment
Я также сделал первый (с шаблонами php ‹?php echo $view['form']-›errors($form) ?›), но он все равно пустой! - person putolaruan; 08.08.2011
comment
@mives Вы должны установить error_bubbling в значение true в типе формы, явно установив параметр для каждого поля. - person kgilden; 10.09.2011
comment
Если вы используете собственные валидаторы, Symfony не возвращает ошибки, сгенерированные этими валидаторами, в $form->getErrors(). - person Jay Sheth; 14.03.2014
comment
Вы также можете сделать $form->getErrors(true), чтобы включить ошибки дочерних форм. - person Chris; 29.03.2015
comment
Даже после установки error_bubbling на true для каждого из моих полей оба {{ form_errors(form) }}, $form->getErrors(true) и $form->getErrorsAsString() ничего не возвращают (пустой массив или строка). - person DarksteelPenguin; 03.06.2016

Симфони 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);

Симфони 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)

person Flip    schedule 02.07.2013
comment
Это выглядит как самый правильный ответ для текущей версии Symfony 2.4. - person Slava Fomin II; 28.04.2014
comment
@Flip it отлично работает на 2.5 - person iarroyo; 29.08.2014
comment
Отличный ответ, НО $errors[$child->getName()] = $this->getErrorMessages($child); вызывал исключение, так как getErrorMessages отсутствовал в компоненте Symfony\Bundle\FrameworkBundle\Controller\Controller. Поэтому я заменил его на $form_errors[$child->getName()] = $child->getErrorsAsString(); - person Ahad Ali; 13.02.2015
comment
@AhadAli это рекурсивная функция, поэтому, когда вы помещаете фрагмент кода в класс, где вам нужна эта функция, он сможет вызывать сам себя. Ваше исправление не позволит вам получить доступ к вложенным формам. Это сработало еще у 37 человек, должно сработать и у вас ;) - person Flip; 13.02.2015
comment
@Flip Ах, извините, мой плохой, я только смотрел на $this->getErrorMessages() и думал, что он вызывается непосредственно внутри контроллера и является частью API Symfony. - person Ahad Ali; 16.02.2015

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

Симфония 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;
}
person Icode4food    schedule 21.11.2011
comment
Улучшен gist.github.com/2011671, но все же не то, что мне нужно. Я хочу, чтобы ключи массива были именами полей, но это не так. - person umpirsky; 10.03.2012
comment
@SalmanPK Twig нигде не упоминается в приведенном выше коде. Я не верю, что понимаю ваш комментарий. - person Icode4food; 03.07.2012
comment
Вот исправление для предыдущей сути, это работает в Symfony 2.1.7. gist.github.com/WishCow/5101428 - person K. Norbert; 06.03.2013
comment
Похоже, что в вашем образце в Symfony2.1 есть опечатка $this->getFormErrors должно быть $this->getErrorMessages - person Mick; 26.03.2013
comment
@umpirsky Чтобы получить имя поля, я получил это: $child->getConfig()->getOptions()['label'] Мне потребовалась целая вечность, чтобы узнать... - person jsgoupil; 04.11.2013
comment
Это, безусловно, лучший ответ здесь, многие другие ответы просто неверны или проверяются несколько раз :). Сбор ошибок сложен, потому что формы построены как деревья. Если вы хотите узнать, почему стоит этот ответ или о переменных формы и выполнении дополнительной пользовательской визуализации, я попытался сказать больше здесь: knpuuniversity.com/blog/symfony-debugging-form-errors - person weaverryan; 20.01.2014

Используйте средство проверки, чтобы получить ошибки для определенного объекта

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:

person Olivier 'Ölbaum' Scherler    schedule 14.09.2011
comment
Я не уверен, что это хороший подход к проверке каждого объекта по отдельности. Что делать, если у вас сложная иерархическая форма? Вторая проблема заключается в том, что валидация происходит дважды. - person Slava Fomin II; 28.04.2014
comment
@SlavaFominII - Вторая проблема заключается в том, что проверка происходит дважды - Хорошо, ничего не обновляется! Тот же список ошибок после! - person BentCoder; 16.07.2014

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

person Cedo    schedule 04.02.2015
comment
Хороший, с использованием того же материала. - person Damaged Organic; 23.04.2015
comment
не так ли? :) @KidBinary - person Cedo; 24.04.2015
comment
Абсолютно прекрасно, приятель :) - person Damaged Organic; 24.04.2015
comment
Лучший вариант: $errors = array_map(function ($item) { return $item-›current()-›getMessage(); }, $campaignForm-›getErrors(true, false)); - person Enrique Quero; 15.06.2016
comment
Хорошее решение для Symfony 2.7 - person Yann Chabot; 21.04.2018

Для моих флеш-сообщений меня устраивало $form->getErrorsAsString()

Редактировать (от Benji_X80): для SF3 используйте $form->getErrors(true, false);

person Tjorriemorrie    schedule 20.09.2012
comment
Я знаю, что это старый ответ, но на будущее: This method should only be used to help debug a form. (источник) - person cheesemacfly; 12.05.2013
comment
getErrorsAsString() устарела в версии 3.0, используйте: $form->getErrors(true, false); - person Benji_X80; 12.04.2016

Функция для 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;
}
person stwe    schedule 28.03.2013
comment
Я собирался опубликовать новый ответ на этот пост, но вы, похоже, опередили меня. Мне пришлось просмотреть исходный код, чтобы понять, почему не были найдены вызовы методов. - person Dr.Knowitall; 13.08.2013
comment
Я заметил, что это не извлекает ошибки из элементов, для которых для всплывающей подсказки установлено значение true. SF2.4 - person kinghfb; 28.04.2014
comment
@stwe, какова цель первого утверждения IF? Почему это взаимоисключающее? Насколько я понимаю, у формы могут быть как собственные ошибки, так и дочерние элементы. - person Slava Fomin II; 28.04.2014

Переведенные сообщения об ошибках формы (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 разных сообщений:

person Mick    schedule 26.03.2013
comment
Только что попробовал вашу версию, и она пошла Fatal Error: Call to undefined method Symfony\Component\Form\FormError::getMessagePluralization(). Я подозреваю, что это только для Symfony 2.1? - person Czar Pino; 15.04.2013

СИМФОНИ 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."
}
person sbouba    schedule 07.02.2017

Для 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));
person Anjana Silva    schedule 24.03.2018

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

$errors = $this->get('validator')->validate($user);
person antoinet    schedule 23.08.2011
comment
Это проверит объект, но не форму. Если, например, токен CRSF был причиной ошибки, сообщение не будет включено. - person Icode4food; 21.11.2011

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

}
person Jay Sheth    schedule 14.03.2014

Переведенные сообщения об ошибках формы (Symfony2.3)

Мой вариант решения проблемы:

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

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

/src/Acme/MyBundle/Форма/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/Контроллер/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();
        }
    }
person Lebnik    schedule 26.11.2013

Основываясь на ответе @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 родительской формы.

person RobbeR    schedule 07.04.2016

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

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

person PrancingPonyDev    schedule 23.02.2021

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

}
person Hard-Boiled Wonderland    schedule 09.07.2013

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

person ahyong    schedule 28.10.2013

Я придумал это решение. Он отлично работает с последней версией 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;
}

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

person Slava Fomin II    schedule 28.04.2014
comment
@weaverryan, не могли бы вы взглянуть на мое решение, пожалуйста? Это действительно, или есть недостатки или какие-либо заблуждения? Спасибо! - person Slava Fomin II; 28.04.2014

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

Надеюсь помочь

person Shigiang Liu    schedule 02.12.2016

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

        )

)

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

person Krzysztof Trzos    schedule 29.02.2020

Для 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;
}
person Fernando P. G.    schedule 22.05.2013