Dans ce billet nous allons voir comment utiliser Ajax pour valider champ par champ un formulaire constuit avec sfForm de Symfony 1.1.
Je prend pour acquis que vous ayez lu / que vous connaissez la validatation de formulaire « classique » offert par sfForm. Tout est expliqué là et surtout là.
En fait le souci avec les validateurs symfony 1.1 est qu’ils ne possèdent pas la méthode isValid() contraitement aux validateurs symfony 1.0. Du coup, quand on fait de l’ajax, nous sommes obligés de passer d’un type de validateur à un autre.
Cette exemple illuste le fait avec le validateur sfValidatorEmail (sf 1.1) et sfEmailValidator (sf 1.0). L’idée est de définir les paramêtres du validateur (les messages d’erreurs dans notre cas) qu’à un seul endroid afin de respecter le concept Dry (Don’t Repeat Yourself).
Voilà les étapes nécessaires pour mettre en place une validation de formulaire classique et « Ajaxé » :
- Vous créez vos widgets dans lib/form/MaClasseForm.class.php
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
class MaClasseForm extends BaseMaClassForm { public function configure() { $this->setWidgets(array( 'id' => new sfWidgetFormInputHidden(), 'FirstName' => new sfWidgetFormInput(), 'LastName' => new sfWidgetFormInput(), 'Email' => new sfWidgetFormInput(), 'password_repeat' => new sfWidgetFormInput(), 'password' => new sfWidgetFormInput() )); // On peut modifier les labels si besoin est ... $this->widgetSchema->setLabels(array( 'FirstName' => 'Prenom', 'Email' => 'addresse Email ', 'LastName' => 'Nom', )); } }
- Vous définissez vos validateurs dans lib/form/MaClasseForm.class.php à la suite de la méthode configure()
1 2 3 4 5 6 7 8 9 10 11 12 13
$this->setValidators(array( 'id' => new sfValidatorPropelChoice(array('model' => 'Utilisateurs', 'column' => 'id', 'required' => false)), 'FirstName' => new sfValidatorString(array('max_length' => 20, 'required' => false)), 'LastName' => new sfValidatorString(array('max_length' => 20, 'required' => false)), 'password' => new sfValidatorString(array('max_length' => 40, 'required' => false)), 'password_repeat' => new sfValidatorString(array('max_length' => 40, 'required' => false)), 'Email' => new sfValidatorEmail(array('required' => true)), 'Birthday' => new sfValidatorDate(array('required' => false)), )); // J'en profite pour definir un nom formater des champs pour avoir $_POST['utilisateurs['email']] etc $this->widgetSchema->setNameFormat('utilisateurs[%s]');
- Normallement à ce stade vous pouvez utiliser la classe de formulaire dans votre fichier apps/MonApp/MonModule/actions/actions.class.php
1 2 3 4 5 6 7 8 9 10 11 12 13
public function executeMonAction() { $this->formInscription = new MaClasseForm(); if ($this->request->isMethod('post')) { // récupère les champs du formulaire sous forme de tableau. $array_champ = $this->getRequest()->getParameter("utilisateurs"); $this->formInscription->bind($array_champ); if ($this->formInscription->isValid()) { // Formulaire valide. } }
- Voilà la validation « classique » et faite. Et l’ajax dans tout ça ? On y vient ^^ Alors au lieu d’afficher le formulaire de manière classique (via le template adéquate apps/MonApp/MonModule/templates/MonActionSuccess.php)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
// Au lieu de echo $form; // On va rendre le template comme on le souhaite // Par souci de flemme je ne fait que le champ Email <table> <tr> <td><?php echo $form['Email ']->renderLabel() ?>:</td> <td><?php echo $form['Email '] ?></td> <td><span id="error_for_email "> <?php if ($form['Email ']->hasError()): ?> <ul class="error_list"> <?php foreach ($form['Email ']-<getError() as $error): ?> <li class="error_for"><?php echo $error ?></li> <?php endforeach; ?> </ul> <?php endif; ?> </span></td> </tr> </table>
- Et maintenant on va jouer avec l’ajax pour vérifier ce champ email lors de la saisie.
1 2 3 4 5 6 7 8 9 10
/************** Ajax Time :) *****************/ // Dans le template on met : <?php // Crée l'observateur pour le champ utilisateurs_email du formulaire. echo observe_field("utilisateurs_email",array( 'update' => "error_for_email", 'url' => "MonModule/VerifierEmail", 'with' => "'email='+ value", )); ?>
- On rajoute l’action VerifierEmail() dans le fichier d’actions.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40
public function executeVerifierEmail() { // Initialisation des variables internes $chaine_a_verifier = $this->getRequestParameter("email"); $array_erreur = array(); // Récupère le validateur d'email définit dans lib/form/MaClasseForm.class.php $form = new MaClasseForm(); $validateur_form = $form->getValidatorSchema(); $all_validateurs_field = $validateur_form->getFields(); $email = $all_validateurs_field["email"]; // Récupere le bon code d'erreur if($chaine_a_verifier == "") { $array_erreur = array('email_error' => $email->getMessage("required")); } else { $array_erreur = array('email_error' => $email->getMessage("invalid")); } // Crée un validateur d'email hérité de Symfony 1.0 $validateur_email = new sfEmailValidator($this->getContext()); // Un peu de Dry (Don't Repeat Yourself) afin d'éviter d'avoir des messages d'erreurs définit à deux endroits différents. $validateur_email->initialize($this->getContext(), $array_erreur ); // Dispatcheur vers le bon templates if($validateur_email->execute($chaine_a_verifier,$message_retour)) { $message_a_afficher = $this->getPartial("ajax_error",array("message"=>$message_retour,"reussi"=>true)); } else { $message_a_afficher = $this->getPartial("ajax_error",array("message"=>$message_retour,"reussi"=>false)); } return $this->renderText($message_a_afficher); }
- Donc en fait on met à jour le « span » error_for_email avec le résultat de l’appel de la méthode VerifierEmail(). J’ai utilisé un template partial car le but est de le réutiliser pour tous les appels en Ajax. Celui ci n’est pas bien compliqué
1 2 3 4
<ul> <li class="<?php if($reussi) echo "accept_for"; else echo "error_for"; ?>">&nbsp;<?php echo $message; ?> </li> </ul>
- Et voilà, le formulaire fonctionne classiquement et dispose de vérifications en Ajax aussi.
Nous venons de voir le validateur d’Email, mais tous fonctionnent sur le même principe. Voici la méthode pour utiliser le validateur de Sting qui diffère un peu.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 | public function executeVerifierPseudo() { // Initialisation des variables internes $chaine_a_verifier = trim($this->getRequestParameter("pseudo")); // Récupère le validateur de pseudo $form = new MaClasseForm(); $validateur_form = $form->getValidatorSchema(); $all_validateurs_field = $validateur_form->getFields(); $validateurs = $all_validateurs_field["FirstName"]; $pseudo = $validateurs->getValidators(); $pseudo = $pseudo[0]; // Crée un validateur de String sf1.0 $validateur_pseudo = new sfStringValidator($this->getContext()); $validateur_pseudo->initialize($this->getContext(), array( 'min' => $pseudo->getOption("min_length"), 'min_error' => $pseudo->getMessage("min_length"), 'max' =>$pseudo->getOption("max_length"), 'max_error' => $pseudo->getMessage("max_length"), 'required' => $pseudo->getMessage("required"), )); // Dispatcheur vers le bon templates if($validateur_pseudo->execute($chaine_a_verifier,$message_retour) && $chaine_a_verifier != "") { $message_a_afficher = $this->getPartial("ajax_error",array("message"=>$message_retour,"reussi"=>$bool)); } else { if($chaine_a_verifier == "") $test = $pseudo->getMessage("required"); // Remplace les jokers des messages d'erreurs. $min = $pseudo->getOption("min_length"); $max = $pseudo->getOption("max_length"); $message_retour= preg_replace("#%value%#",$chaine_a_verifier,$message_retour); $message_retour= preg_replace("#%min_length%#",$min,$message_retour); $message_retour= preg_replace("#%max_length%#",$max,$message_retour); $message_a_afficher = $this->getPartial("ajax_error",array("message"=>$message_retour,"reussi"=>false)); } return $this->renderText($message_a_afficher); } |
Et voilà c’est fini :)







le 27 juillet 2009 à 16h27
Hello, merci pour cet article. si j’ai bien compris, $validateurs = $all_validateurs_field["FirstName"] renvoie une instance de la classe ValidatorBase. Or dans cette classe, il n’y a pas de methode getValidators(). Dans ce cas, comment cette variable peut elle être renseigée $pseudo = $validateurs->getValidators(); puisque la methode getValidator n’est pas implémentée dans la classe Validatorbase ?
le 27 juillet 2009 à 17h10
Salut Xavier, sympathique de déterrer des articles vieux d’un an ^^
Je viens de relire ce que j’ai écrit et il apparait que j’utilise pas ta méthode getValidators, j’utilise la méthode getMessage .. comme là : $email->getMessage(« required ») … $email est déjà un sfValidatorEmail .. http://www.symfony-project.org/api/1_2/sfValidatorEmail
Je comprend pas trop ce que tu cherches à me dire ^^
Et sinon c’est du symfony 1.1 et sans doute tout à changer en 1.2 depuis :p Les formulaires changent de forme à chaque nouvelle version :p
le 28 juillet 2009 à 10h06
Hello et merci pour la réponse. Je parlais du code de la dernière partie (8) pour la vérification du pseudo. Plus précisément cette ligne : $pseudo = $validateurs->getValidators(); Cette methode n’est pas (plus ?) implémentée dans la classe sfValidatorBase de Symfony 1.2. Peut être que je n’ai pas bien saisi ton code, quoi qu’il en soit, je n’arrive pas à me servir des validateurs Symfony pour une validation ajax, je suis pour le moment contraint de les reproduire en javascript.
le 28 juillet 2009 à 11h24
Ah oui, le code était plié j’avais pas vu :p MéaCulpa, en fait l’exemple que je donne est faux. (sans dec ^^)
En fait ce tuto est extrait d’un projet, et la magie des copiés collés et la légèreté des tests ont fait qu’en fait le champ « Firstname » était dans le projet un « new sfValidatorAnd » donc getValidators() marchait, donc récupération sous forme de tableau du validateur de String bref .. voilà
Sinon y me reste le code du module d’inscription au complet sur : http://svn.assembla.com/svn/GreenHouse/Application_Web/apps/website/modules/Inscription/actions/actions.class.php
et la classe de formulaire : http://svn.assembla.com/svn/GreenHouse/Application_Web/plugins/sfGuardPlugin/lib/form/sfGuardUserForm.class.php
Oui je sais, je triturais le code du plugin au lieu de l’étendre, c’est moche c’est horrible, je culpabilise. Mais je t’ai pas dit que ce projet n’est jamais sortie, que c’est resté du test ?
Voilà voilà normallement avec le svn tu as tout :p
le 29 juillet 2009 à 10h01
merci pour ces explications et pour les sources, c’est super sympa. En ce qui concerne mon projet, j’ai pour l’instant fait deux validations différentes, une en JS (Plugin JQuery Validation) et l’autre, côté serveur, en me servant de la validation Symfony. ça m’oblige à avoir deux fois les règles de validation. C’est pas très propre mais pour le moment ça marche. Dommage que Symfony ne gère pas mieux ce genre de choses.
le 29 juillet 2009 à 10h13
Merci, je ne connaissais pas Plugin JQuery Validation :p
Ben d’un coté je connais pas de framework qui gère les mêmes règles de validation js/serveur… ah si peut être la plateforme .net (qui génère du js ..) à vérifier.
D’un autre coté, si tu génère ton JS en php à partir de validators perso qui puisse te renvoyé l’expression régulière de test .. tu peux arrivé à un truc … m’enfin c’est pas le pied non plus ..
Ou alors as tu regard » si un plugin symfony n’existe pas déjà ?
Je viens de le faire et suis tombé sur http://www.symfony-project.org/plugins/sfJqueryFormValidationPlugin qui a l’air de faire ce que je viens de dire (cad parser ton schéma de validation, créer le js qui va bien), à tester ..
le 29 juillet 2009 à 11h20
Bon je viens de tester http://www.symfony-project.org/plugins/sfJqueryFormValidationPlugin il valide les strings, les emails, les dates mais c’est pas en ajax, c’est juste du JS. Mais il fait tout tout seul, tu peux le restreindre sur certains form, mais globalement wahou quoi.
Après c’est à toi de voir si tu peux arriver à le customiser pour tes besoins et rajouter de l’ajax avec sa base :p Et si t’arrives à qqchose de propre fais le connaitre à la communauté :p