ZendFramework et la gestion des erreurs de formulaire

Tagged:

Bon je suis un peu déçu parce que j'ai rien trouvé ... Sans aller jusqu'a QuickForm qui pour moi a une grosse erreur de conception de base (mélanger le modèle et la vue c'est vraiment crado et chiant a gérer sur la durée je trouve ...) et que je n'utiliserai donc pas, j'ai cherché brievement quelque chose qui gère ça et seulement ça ... a croire que ça n'éxiste pas ou que je cherche très mal (c'est fort possible remarque !)

Bref je voulais pas réinventer Prado et faire un truc super balaise, simplement gérer correctement les erreurs de mes formulaires ...

Donc voila une classe qui parait assez simple, avec un singleton multiple et la gestion basique d'un array ...

class Form_Messages {
	
	private static $instances = null;
	public static function getInstance($formName) {
		if (self::$instances == null) {
			self::$instances = array();
			self::$instances[$formName] = new Form_Messages();
		}
		return self::$instances[$formName];
	}
	
	private $messages = null;
	public function __construct() {
		$this->messages = array();
	}
	
	public function message($for) {
		if (array_key_exists($for, $this->messages)) {
			return $this->messages[$for];
		}
		return "";
	}
	
	public function addMessage($for, $message) {
		$this->messages[$for] = $message;
	}
	
	public function notEmpty() {
		return !empty($this->messages);
	}
	
}

Bon c'est pas très compliqué ça ... On va s'en servir dans la vue, alors j'ai fait un ViewHelper pour pouvoir y accéder ...

class Zend_View_Helper_Message {
    
    public function message($for) {
		$view = Zend_Registry::get('view');
		if (!isset($view->formName)) {
			throw new Exception('View has no attribute formName');
		}
    	return Form_Messages::getInstance($view->formName)->message($for);
    }
    
}

Donc c'est basé sur l'attribut formName de la vue, qui au moment du rendering doit être à la bonne valeur, mais ça c'est plutot normal ! Si vous découpez bien votre truc, vous n'aurez jamais de soucis avec ça, sinon il faut updater le code ici !

Si on prend par exemple un bean User assez simple avec une methode validate qui fait ce qu'il faut pour enregistrer les messages du formulaire:

class Bean_User extends Bean {
	
	public $id = -1;
	public $nick_name = '';
	public $password = '';
	public $mail = '';
	public $first_name = '';
	public $last_name = '';
	public $user_type = 'User';
	
	public function validate(Form_Messages $messages) {
		if (!$this->nick_name) $messages->addMessage('nick_name', 'Should set the nickname');
		if (!$this->password) $messages->addMessage('password', 'Should set the password');
 
		if (!$messages->notEmpty() && !$this->nickNameUnique()) $messages->addMessage('nick_name', 'Nickname is already used !');
	}
	
	private function nickNameUnique() {
		$userModel = new Model_User();
		$resultset = $userModel->findByNickName($this->nick_name);
		$users = $resultset->toArray();
		return empty($users);
	}
	
}

et dans mon controlleur qui fait tout le boulot et qui va valider l'utilisateur :

				$messages = Form_Messages::getInstance($view->formName);
				$user->validate($messages);
				$error = $messages->notEmpty();

Bon jusque là ça peut paraitre un peu lourd, mais à mettre en place, ça m'a mit 20 minutes, et a réutiliser ça met même pas 20 secondes ... donc c'est assez pratique ...

Voila comment je construit ma form par exemple :

<form method="post" name="<?= $this->formName ?>">
<?php
	echo $this->i18n('nick_name');
	echo ': '; 
	echo $this->formText('nick_name', $user->nick_name);
	echo $this->message('nick_name');
	echo "<br />";
?>
</form>

un bout d'exemple hein ... voila ! Ca fonctionne, c'est réutilisable, on peut même avoir plusieurs formulaires avec chacun ses messages... enfin tout un tas de réjouissances :p

Si vous avez d'autres idées ou des implémentations plus complète que vous connaissez je suis assez intéressé !

Comments

Pour (peut-être) te faire baver, regarde un peu comment ce type de choses sont gérées dans Symfony : http://tinyurl.com/ytoolv

Ce qui me gène moi dans Symfony et CakePhp, c'est que pour vraiment profiter de la puissance du truc, il faut lancer quelques lignes de commandes dans la console.
Ce n'est pas compliqué, mais lourd.
Ah moins qu'il n'y ai des astuces ?

Moi ce qui me gène dans Symphony, c'est que quand je lis la doc, j'ai l'impression de faire du JSF ! C'est beaucoup moins straightforward que ZF ... D'autant que le modèle objet y es moins bien respecté (même si c'est un argument qui n'a rien a voir mais j'aime moins ...)

Dans ZF, la complexité, c'est toi qui l'ajoute... le seul truc qu'ils te filent c'est un outil pour faire du MVC après ... tu veux mettre des templating system, des propel, des abstractions ou design pattern, tu choisis, mais de base rien n'est obligatoire ... tu peux même te permettre de faire un projet sans MVC et utiliser le ZF comme PEAR par exemple !

Enfin j'ai commencé un projet avec le ZF, si je teste symphony plus en détail, je vais abandonner mon projet me connaissant, donc je continue comme ça ! =) symphony suivra !

C'est le problème que j'ai aussi. En général après avoir commencé un projet, je découvre une nouvelle manière de le faire, et je perds plus de temps a découvrir qu'à développer.

> D'autant que le modèle objet y es moins bien respecté

Hmmm. Tu peux développer ? :)

Oué je nuance un peu, le modèle objet de validation tout ça c'est impec (un peu compliqué a mon gout, mais disons que la complexité apporte souvent la fonctionalité) mais ce que j'aime moins, c'est toutes les fonctions globales qui sont associées à aucun objet ...

Enfin dans un projet qui n'est pas PHP5/Objet ça dérange pas, mais le concept fonction/variable globale associé au superobjet ... beuh ...

Je pense surtout au $sf_request, input_tag(), use_helper('Date') ...

$this->request, $this->input(), ou $this->date() ... Tout ça c'est possible et ça contribue a la simplicité et l'utilisabilité ...

D'autant que du coup tu sais que le $this est un objet Zend_View et tu sais ou chercher si tu veux savoir toutes les fonctionnalités qui te sont offertes ...

Enfin c'est personnel, mais je trouve ça mieux foutu dans le ZF !