Afficher les erreurs de validation en entête de formulaire
Sur un formulaire comportant de nombreux champs, il peut être utile de regrouper l’ensemble des messages d’erreur en entête, l’utilisateur ayant ainsi accès d’un coup d’oeil à l’ensemble des erreurs. Cela peut également être utile lorsque certains champs sont trop petits pour y afficher un message d’erreur, l’utilisateur voyant alors que le champ est mal rempli mais ne sachant pas pourquoi. Voyons comment récupérer l’ensemble des messages d’erreur de validation d’un Modèle, et comment les afficher sur un formulaire.
Récupérer les messages de validation d’un Modèle
Les différents messages d’erreur sont stockés dans la variable validationErrors du Modèle. Dans le cas d’un enregistrement unique, le contenu de validationErrors est :
$this->validationErrors = array( 'champ1' => 'message1', 'champ2' => 'message2');
Dans le cas d’enregistrements multiples, les clés sont les id des enregistrements et les valeurs les paires champ / message :
$this->validationErrors = array( '1' => array( 'champ1' => 'message1', 'champ2' => 'message2'), '2' => array( 'champ1' => 'message1', 'champ2' => 'message2'));
Nous allons ajouter dans le AppModel une fonction error_messages qui renvoie un tableau contenant la liste de l’ensemble des messages d’erreur, en s’assurant qu’un même message présent plusieurs fois dans validationErrors ne soit présent qu’une fois dans le tableau final.
// {app}/app_model.php function error_messages() { if (empty($this->validationErrors)) return array(); $messages = array(); foreach ($this->validationErrors as $key => $val) { if (is_numeric($key)) { foreach ($val as $k => $v) { $messages[] = $v; } } else { $messages[] = $val; } } return array_unique($messages); }
Afficher les erreurs dans un formulaire
Nous allons créer un MyFormHelper héritant du FormHelper de CakePHP. Celui-ci nous permettra de bénéficier de toutes les méthodes du FormHelper, tout en rajoutant notre méthode d’affichage d’erreurs.
// {app}/views/helpers/my_form_helper.php App::import('Helper', 'Form'); class MyFormHelper extends FormHelper { function errors($model) { $model = Inflector::camelize($model); $this->setEntity($model); $errors = array(); if (ClassRegistry::isKeySet($model)) { $object =& ClassRegistry::getObject($model); $errors = $object->error_messages(); } if (empty($errors)) { return false; } $out = '<div class="error-messages">' . "\n"; $out .= '<h2>Veuillez corriger les erreurs mentionnées ci-dessous</h2>' . "\n"; $out .= '<ul>' . "\n"; foreach ($errors as $error) { $out .= "<li>$error</li>\n"; } $out .= '</ul>' . "\n"; $out .= '</div>' . "\n"; return $out; } }
La méthode errors récupère les messages d’erreur du modèle demandé et les renvoie sous forme de liste dans une div. Il faut ensuite inclure notre Helper depuis le AppController :
// {app}/app_controller.php class AppController extends Controller { var $helpers = array('MyForm'); }
Enfin, nous allons utiliser dans la vue non plus la variable $form d’origine mais $myForm :
// {app}/views/posts/add.ctp
<h1>Nouveau post</h1>
<?php echo $myForm->errors('Post') ?>
<?php echo $myForm->create('Post') ?>
<?php echo $myForm->inputs() ?>
<?php echo $myForm->end('Ajouter') ?>Voici le résultat d’un formulaire comportant des erreurs de validation :

Commentaires
2 février 2009 à 14:49
Sinon pourquoi ne pas passer le ‘error’ à false dans la vue sur chaque input et regrouper les $form->error en entête du formulaire ? Moins de surcharge, plus flexible.
2 février 2009 à 15:16
C’est toujours pratique d’avoir les validations indiquées à côté des champs en terme d’utilisabilité, c’est un reproche qui a été fait à Rails au début, où par défaut les erreurs ne s’affichent qu’en entête.
On peut aussi regrouper les $form->error en entête, mais cela oblige à boucler sur tous les champs du formulaire et vérifier ceux qui ont effectivement une erreur. Si on a à faire cela sur plusieurs formulaire, c’est vite pénible et on se retrouve à mettre la logique dans un element ou un helper
4 février 2009 à 8:19
Bonjour, n’y a-t-il pas plus simple en récupérant les erreurs depuis l’appel à Model::invalidFields() ?
Voici un article qui propose une solution à mon sens plus élégante (basée sur un élément et cette fameuse méthode) : http://www.milesj.me/blog/read/10/displaying-form-errors-as-a-list-in-cakephp
4 février 2009 à 17:58
Passer par un élément est peut-être en effet plus simple, j’ai préféré ici ajouter une méthode au pour garder une consistance dans la vue.
Par contre dans l’article cité :
on appelle deux fois invalidFields (une fois dans validates() et une fois avec invalidFields(), un simple $errors = $this->User->validates() suffirait
surtout le cas où le formulaire présente plusieurs enregistrements d’un même modèle n’est pas géré puisqu’alors les erreurs retournées sous la deuxième forme présentée dans l’article
Du coup on doit également filtrer les messages uniques et on retrouve dans l’élément le même code que celui actuellement dans la méthode error_messages.
J’ai préféré une méthode de modèle, celle-ci pouvant faire l’objet de tests unitaires, objet d’un prochain article..
4 février 2009 à 19:25
Bien vu en effet ! Je n’avais pas pensé aux enregistrements multiples d’un même modèle …
Je garde ta solution de côté pour la fois où je devrai implémenter ceci.
3 juin 2009 à 21:34
Tout d’abord merci pour ce blog.
Je me suis mis à Cake il y a quelques temps et je me délecte des articles sur CakePHP sur ce blog et d’autres également.
Dans l’exemple, je remarque que la méthode error_messages() de l’objet me jette une erreur SQL. Je ne sais d’ailleurs si elle existe vraiment.
Quoiqu’il en soi j’ai remplacé par
$object->validationErrors
qui me sort bien les erreurs générées.
Voilà !
4 juin 2009 à 8:23
@Benjamin
je n’arrive pas à reproduire ce message d’erreur avec la dernière version de CakePHP (v1.2.3.8166). As-tu bien mis la méthode errormessages() dans appmodel.php ?
9 juin 2009 à 8:41
Oui la même version que toi… bizarre
Enfin le principal c’est que ta méthode marche bien et que je suis arrivé à mes fins ! ^^
merci
10 juin 2009 à 16:11
Salut !
je viens de mettre en place ce helper avec succès !
j’aimerais que les messages d’erreur ne s’affichent qu’en en-tête, comment « vider » les messages d’origine ou éviter qu’ils ne s’affichent avec leur champ associé ?
merci d’avance
12 juin 2009 à 8:03
@Elianora lorsque tu affiches un input tu peux utiliser la clé ‘error’ à false pour en supprimer le message d’erreur :
12 juin 2009 à 8:58
merci, j’avais complètement oublié cette option !
16 février 2010 à 1:17
Bonjour, Tout d’abord je tenais à vous remercier pour votre blog, il contient moult tutoriels et astuces très appréciables ! Cependant, je rencontre actuellement un soucis, j’aimerais obtenir des messages d’erreurs traduits.
Ma vue est donc construite avec un formulaire contenant des éléments comme celui-ci :
Mon modèle ressemble à ceci :
Et enfin, voici la méthode add de mon contrôleur :
Le problème étant qu’aucune erreur ne s’affiche, j’éprouve quelques difficultés à trouver mes étourderies. Merci
16 février 2010 à 9:56
Dans le $validate du modèle, vous n’avez pas renseigné la clé ‘message’, qui est justement utilisée par le $form->input dans la clé ‘error’, il faut donc ajouter ‘message’ => ‘emptyField’ dans la règle de validation.
16 février 2010 à 12:48
En effet, sa fonctionne, merci !