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 :

Résultat d'affichage des messages d'erreur

Matthieu Sadouni

Articles connexes

Commentaires

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.

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

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

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

$this->validationErrors = array(
    '1' => array(
        'champ1' => 'message1',
        'champ2' => 'message2'),
    '2' => array(
        'champ1' => 'message1',
        'champ2' => 'message2'));

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

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.

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à !

@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 ?

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

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

@Elianora lorsque tu affiches un input tu peux utiliser la clé ‘error’ à false pour en supprimer le message d’erreur :

echo $form->input('Model.field', array('error' => false));

merci, j’avais complètement oublié cette option !

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 :

echo $form->input('Contact.name', array('label' => false,
                                        'div' => false,
                                        'class'   => 'contactForm',
                                        'size'    => 25,
                                        'error'   => array(
                                            'emptyField' => __('Veuillez saisir votre nom', true)
                                        )
                                    ));

Mon modèle ressemble à ceci :

var $validate = array(
        'name' => array(
            'emptyField'  => array(
                'required'        => true,
                'allowEmpty'  => false
            )
        ),

Et enfin, voici la méthode add de mon contrôleur :

if ($this->Contact->save($this->data)) {
            $this->Session->setFlash('Message envoyé !', 'growl');
            $this->redirect(array('action'=>'index'));
        } else {
            $this->Session->setFlash('Erreur !', 'growl', array('type' => 'error'));
        }

Le problème étant qu’aucune erreur ne s’affiche, j’éprouve quelques difficultés à trouver mes étourderies. Merci

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.

En effet, sa fonctionne, merci !

Participez

Pour insérer une portion de code, utilisez <pre lang="php">...</pre>