Symfony2: comment obtenir les erreurs de validation du formulaire après avoir lié la requête au formulaire

Voici mon code saveAction (où le formulaire transmet les données)

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

ma question Est: Comment puis-je obtenir les erreurs si $form->isValid() retourne false ?

104
demandé sur Trix 2011-08-08 10:58:58

19 réponses

vous avez deux façons possibles de le faire:

  • ne pas rediriger l'utilisateur sur l'erreur et l'affichage {{ form_errors(form) }} au sein de fichier de modèle
  • erreur d'accès à la matrice $form->getErrors()
110
répondu nefo_x 2015-05-24 23:40:29

Symfony 2.3 / 2.4:

cette fonction récupère toutes les erreurs. Ceux sur le formulaire comme "le jeton CSRF est invalide. Veuillez essayer de soumettre de nouveau le formulaire."ainsi que des erreurs supplémentaires sur les enfants de forme qui n'ont pas bubbling erreur.

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

pour obtenir toutes les erreurs sous forme de chaîne de caractères:

$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 (en bas: The method Form::getErrorsAsString() was removed )

93
répondu Flip 2014-12-15 21:02:27

ci-Dessous est la solution qui a fonctionné pour moi. Cette fonction est dans un contrôleur et retournera un tableau structuré de tous les messages d'erreur et le champ qui les ont provoquées.

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 et plus récent:

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;
}
47
répondu Icode4food 2013-03-26 13:37:05

utilisez le validateur pour obtenir les erreurs pour une entité spécifique

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

référence API:

33
répondu Olivier 'Ölbaum' Scherler 2013-12-12 11:42:19

pour obtenir des messages (traduisibles) appropriés, Utilisant actuellement SF 2.6.3, voici ma fonction finale (comme aucun de ceux ci-dessus semblent fonctionner plus):

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

comme la méthode Form::getErrors() retourne maintenant une instance de FormErrorIterator , à moins que vous commutez le second argument ($flatten) en true . (Il retournera alors une instance FormError , et vous devrez appeler la méthode getMessage () directement, sans la méthode 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;
}

)

la chose La plus importante est en fait de définir le premier argument à true afin d'obtenir les erreurs. En laissant le second argument ($flatten ) à sa valeur par défaut ( true ) retournera FormError instances, tandis qu'il retournera FormErrorIterator instances lorsqu'il est défini à false.

19
répondu Cedo 2015-02-04 14:19:01

la fonction pour symfony 2.1 et plus récente, sans aucune fonction dépréciée:

/**
 * @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;
}
15
répondu stwe 2013-03-28 22:49:22

pour mes messages flash j'étais heureux avec $form->getErrorsAsString()

Edit (from Benjami_x80): Pour le SF3, utiliser $form->getErrors(true, false);

15
répondu Tjorriemorrie 2016-04-13 07:59:02

Traduction Des Messages D'Erreur (Symfony2.1)

j'ai eu beaucoup de mal à trouver cette information donc je pense qu'il est certainement intéressant d'ajouter une note sur la traduction des erreurs de forme.

@Icode4food réponse sera de retour toutes les erreurs d'un formulaire. Cependant, le tableau retourné ne prend pas en compte "message pluralization ou traduction .

vous pouvez modifier la boucle de foreach de @Icode4food réponse pour avoir un combo:

  • Obtenir toutes les erreurs d'une forme particulière
  • retourner une erreur traduite
  • tenir compte de la pluralisation si nécessaire

le voici:

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

Cette réponse a été mis en place à partir de 3 différents postes:

4
répondu Mick 2013-04-15 07:16:01

vous pouvez également utiliser le service de validation pour obtenir des violations de contraintes:

$errors = $this->get('validator')->validate($user);
3
répondu antoinet 2011-08-23 21:05:36

Traduction Des Messages D'Erreur (Symfony2.3)

ma version de résoudre le problème:

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

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

/src/Acme/MyBundle/Form / FormErrors.151970920 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.151970920 php"

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

en Symfony 2.5 vous pouvez obtenir tous les champs erreurs très facile:

    $errors = array();
    foreach ($form as $fieldName => $formField) {
        foreach ($formField->getErrors(true) as $error) {
            $errors[$fieldName] = $error->getMessage();
        }
    }
3
répondu Lebnik 2014-11-21 08:02:42

si vous utilisez des validateurs personnalisés, Symfony ne renvoie pas les erreurs générées par ces validateurs dans $form->getErrors() . $form->getErrorsAsString() retournera toutes les erreurs dont vous avez besoin, mais sa sortie est malheureusement formatée comme une chaîne de caractères, pas un tableau.

la méthode que vous utilisez pour obtenir toutes les erreurs (peu importe d'où elles viennent), dépend de quelle version de Symfony vous utilisez.

la plupart des solutions suggérées impliquent la création d'un fonction qui scanne toutes les formes d'enfant, et extrait les erreurs pertinentes dans un tableau. Symfony 2.3 n'a pas la fonction $form->hasChildren() , mais il a $form->all() .

Voici une classe helper pour Symfony 2.3, que vous pouvez utiliser pour extraire toutes les erreurs de n'importe quel formulaire. (Il est basé sur le code d'un commentaire de yapro sur un billet de bogue LIÉ dans le compte Github de 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;
    }
}

code D'appel:

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
répondu Jay Sheth 2014-04-28 16:28:26

basé sur la réponse de @Jay Seth, j'ai fait une version de la classe FormErrors spécialement pour les formulaires Ajax:

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

d'Utilisation (par exemple dans votre action):

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

version Symfony: 2.8.4

exemple de réponse de 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"
    }]
}

l'objet d'erreur contient le champ" key", qui est l'id de L'élément DOM d'entrée, de sorte que vous pouvez facilement remplir les messages d'erreur.

si vous avoir des formulaires enfant à l'intérieur du parent, n'oubliez pas d'ajouter l'option cascade_validation à l'intérieur du formulaire parent setDefaults .

2
répondu RobbeR 2016-04-07 09:17:22

SYMFONY 3.X

autres SF 3.X méthodes indiquées ici ne fonctionnait pas pour moi parce que je pouvais soumettre des données vides au formulaire (mais j'ai des contraintes NotNull/NotBlanck). Dans ce cas, la chaîne d'erreur ressemblerait à ceci:

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

Qui n'est pas très utile. Alors j'ai fait ceci:

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

qui rendrait que:

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."
}
2
répondu sbouba 2017-02-07 17:51:19

pour Symfony à partir de 2.1 Pour une utilisation avec L'affichage D'erreur de brindille j'ai modifié la fonction pour ajouter une FormError au lieu de simplement les extraire, de cette façon vous avez plus de contrôle sur les erreurs et ne devez pas utiliser error_bubbling sur chaque entrée individuelle. Si vous ne définissez pas de la manière ci-dessous {{ form_errors(form) }} restera vierge:

/**
 * @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);
                }
            }
        }
    }

}
1
répondu Hard-Boiled Wonderland 2013-07-09 20:10:35

$form->getErrors() fonctionne pour moi.

1
répondu ahyong 2013-10-28 07:21:30

j'ai trouvé cette solution. Il fonctionne solide avec le dernier Symfony 2.4 .

je vais essayer de donner quelques explications.

utilisant un validateur distinct

je pense que c'est une mauvaise idée d'utiliser une validation séparée pour valider les entités et les messages de violation des contraintes de retour, comme suggéré par d'autres auteurs.

  1. vous devrez valider manuellement toutes les entités, spécifiez les groupes de validation, etc, etc. Avec des formes hiérarchiques complexes, ce n'est pas pratique du tout et va vite disparaître.

  2. ainsi, vous validerez le formulaire deux fois: une fois avec le formulaire et une fois avec un validateur séparé. C'est une mauvaise idée du point de vue des performances.

je suggère d'itérer récursivement le type de forme avec ses enfants pour recueillir des messages d'erreur.

à l'Aide de certaines méthodes proposées avec exclusif SI l'instruction

certaines réponses suggérées par d'autres auteurs contiennent des déclarations mutuellement exclusives comme celles-ci: if ($form->count() > 0) ou if ($form->hasChildren()) .

autant que je puisse voir, chaque forme peut avoir des erreurs aussi bien que des enfants. Je ne suis pas expert avec Symfony Forms component, mais dans la pratique vous n'obtiendrez pas quelques erreurs du formulaire lui-même, comme CSRF erreur de protection ou champs supplémentaires erreur. Je suggère de supprimer cette séparation.

utilisant une structure de résultat dénormalisée

Certains auteurs suggèrent de mettre toutes les erreurs à l'intérieur d'un simple tableau. Ainsi tous les messages d'erreur de la forme elle-même et de ses enfants seront ajoutés au même tableau avec différentes stratégies d'indexation: basée sur le nombre pour les propres erreurs du type et basée sur le nom pour les erreurs des enfants. Je suggère l'utilisation de structure normalisée des données de la forme:

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"

de cette façon le résultat peut être facilement itéré plus tard.

ma solution

donc voici ma solution à ce problème:

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

j'espère que ça aidera quelqu'un.

1
répondu Slava Fomin II 2014-04-28 19:23:04

Pour Symfony 3.2 et au-dessus de l'utiliser,

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

Utiliser str_replace si vous voulez vous débarrasser de l'ennuyeux ' Erreur: texte " dans chaque description de l'erreur de texte.

$errors[$child->getName()] = str_replace('ERROR:', '', (string) $child->getErrors(true, false));
1
répondu Anjana Silva 2018-03-24 10:13:13

Pour Symfony 2.1:

C'est ma solution finale en regroupant bien d'autres solutions:

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;
}
0
répondu Fernando P. G. 2013-05-22 14:53:54

SYMFONY 3.1

j'ai simplement mis en œuvre une méthode statique pour gérer l'affichage des erreurs

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

en Espérant de l'aide

0
répondu Shigiang Liu 2016-12-02 15:26:39