Symfony2 : Comment obtenir les erreurs de validation du formulaire après avoir lié request 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 la suivante : comment puis-je obtenir les erreurs si $form->isValid() retourne false ?

Il y a deux façons de procéder :

  • ne pas rediriger l’utilisateur en cas d’erreur et afficher {{ form_errors(form) }} dans le fichier template
  • accéder au tableau des erreurs en utilisant $form->getErrors()

Symfony 2.3 / 2.4:

Cette fonction récupère toutes les erreurs. Celles du formulaire comme « Le jeton CSRF n’est pas valide. Please try to resubmit the form » ainsi que les erreurs complémentaires sur les formulaires fils qui n’ont pas error_bubbling.

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

Voici la solution qui a fonctionné pour moi. Cette fonction se trouve dans un contrôleur et renvoie un tableau structuré de tous les messages d’erreur et du champ qui les a provoqués.

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:

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

Utilisez le validateur pour récupérer les erreurs d’une entité spécifique :

if( $form->isValid() )
{
    // ...
}
else
{
    // récupérer la liste des violations de contraintes
    $errors = $this->get('validator')->validate( $user );

    $result = '';

    // parcourir le tableau $errors
    foreach( $errors as $error )
    {
        // Faire des actions avec :
        //   $error->getPropertyPath() : le champ qui a provoqué l'erreur
        //   $error->getMessage() : le message d'erreur
    }
}

Pour SF3 utilisez $form->getErrors(true, false);

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

Utilisez str_replace si vous voulez vous débarrasser du texte « Error : » dans chaque description d’erreur.

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

Vous pouvez également utiliser le service de validation pour obtenir la liste des violations de contraintes :

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

Pour Symfony 5 et 6 ça fonctionne :

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

J’ai obtenu cette sorte de 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."
}