Découverte des formulaires dans symfony 1.1 (2)
Ajout du 22/08/2008 : depuis la parution de cet article, un livre en ligne écrit par Fabien Potencier a vu le jour. Ce livre constitue évidemment la documentation de référence, cet article n'étant que mon expérience personnelle à l'heure ou la documentation n'existait pas.
Suite de mon exploration des formulaires avec symfony 1.1, je vais aujourd'hui tenter de défricher les formulaires autogénérés par Propel, les sfFormPropel.
Cas d'étude : imaginons que nous sommes en train de faire une interface de saisie de commentaires dans quelque chose qui pourrait s'apparenter à un blog. Nous allons essayer d'utiliser au mieux les formulaires propel pour cela.
Nous avons évidemment dans le schema.yml un objet «comment» :
blog_comment:
id:
blog_post_id:
created_at:
author_name: { type: varchar(255), required: true }
author_email: { type: varchar(255), required: true }
title: { type: varchar(255), required: true }
content: { type: longvarchar, required: true }
Si vous faites un «symfony propel:build-all» alors vous ne remarquerez pas quà la différence de symfony 1.0, celui-ci vous fait dans le lot un «symfony propel:build-forms». Qu'est ce donc que cela. C'est tout simple, propel vous génère un fichier de description de formulaire adapaté à votre schema dans le dossier «lib/form». Vous remarquerez qu'à l'instar des fichiers du modèle, il existe un dossier «lib/forms/base» qui contient les classes qui seront regénérées à chaque build-all et que les fichiers de «lib/forms» vous sont destinés.
Reste maintenant dans l'action qui affiche un article de blog à initialiser notre formulaire :
Bien que propel nous ait maché le travail, le résultat n'est pas forcément celui attendu :
Le blog post id ne devrait pas pouvoir être choisi car fixé par défaut au post affiché sur la page. (note au passage, il est nécessaire d'avoir déclaré une méthode __toString à l'objet BlogPost pour générer le drop down menu).
Le champs created_at ne devrait pas non plus pouvoir être modifié car fixé par défaut par symfony au moment de la création de l'enregistrement dans la base.
Il va donc falloir configurer notre formulaire. Le but étant de: - changer le champs blog_post_id en champs hidden avec une valeur fixée par défaut
- ne pas s'occuper ni valider le champs created_at (en gros, l'enlever du formulaire).
Pour cela, il est nécessaire de récupérer le widgetSchema de notre formulaire pour le changer.
Notre action devient alors :
Vous remarquez que nous utilisons l'implémentation array_access de l'objet widgetSchema. Nous pouvons donc accéder aux différents widgets de la même façon que les éléments d'un tableau. Notre formulaire prend alors bien la tête de ce que nous attendons :
Le formulaire passé au peigne fin par la web developer toolbar possède bien juste les champs dont nous avons besoin et notemment le blog_post_id en champs hidden.
Maintenant que ce problème est (pour l'instant) réglé, penchons nous sur la validation de ce formulaire.
Nous allons utiliser la même approche que la dernière fois le formulaire postant par défaut sur la dernière action appelée.
Quelques explications :
Je suis obligé dès l'instanciation de mon formulaire de me débarasser de ce champs pour qu'il ne soit ni affiché ni validé. Nous testons ensuite si nous sommes dans une requête POST. Si c'est le cas alors je vais valider mon formulaire :
Vous remarquez que pour le coup rien de plus simple : cette action :
- clean les entrées
- valide les données
- sauvegarde le nouvel objet commentaire si le formulaire est valide.
Ne me reste qu'à rediriger l'utilisateur si son formulaire est valide :
Si ce n'est pas le cas alors je prépare de nouveau l'environnement de mon template en utilisant la valeur passée dans le formulaire pour retrouver le BlogPost courant.
Testons notre formulaire, nous découvrerons que lorsque je rentre «bleuargh» dans le champs email, mon formulaire est néanmoins validé ... à l'arnaque, au voleur !
Que se passe-t-il, c'est tout simple, notre formulaire ignore que cette valeur est une adresse email. Se basant sur le schema.yml il sait juste que c'est un champs text qui valide «bleuargh» sans aucun soucis. Il convient de changer le validateur associé à ce champs au moment de la validation.
Refactorisons notre code.
Tous ce code de modification de notre formulaire peut sans problème être factorisé dans une classe héritant de BlogCommentForm. Je ne sais pas encore si utiliser «/lib/forms/BlogCommentForm.php» est une bonne idée ou non. J'ai plus plus l'intuition que ce formulaire est utile dans notre module et de laisser l'objet principal inchangé. J'ai donc opté pour utiliser le lib/ de mon module pour surcharger cet objet.
apps/frontend/modules/post/lib/commentForm.class.php
Voila mon action qui devient plus claire, plus lisible :
apps/frontend/modules/post/actions/actions.class.php
Suite de mon exploration des formulaires avec symfony 1.1, je vais aujourd'hui tenter de défricher les formulaires autogénérés par Propel, les sfFormPropel.
Cas d'étude : imaginons que nous sommes en train de faire une interface de saisie de commentaires dans quelque chose qui pourrait s'apparenter à un blog. Nous allons essayer d'utiliser au mieux les formulaires propel pour cela.
Nous avons évidemment dans le schema.yml un objet «comment» :
blog_comment:
id:
blog_post_id:
created_at:
author_name: { type: varchar(255), required: true }
author_email: { type: varchar(255), required: true }
title: { type: varchar(255), required: true }
content: { type: longvarchar, required: true }
Si vous faites un «symfony propel:build-all» alors vous ne remarquerez pas quà la différence de symfony 1.0, celui-ci vous fait dans le lot un «symfony propel:build-forms». Qu'est ce donc que cela. C'est tout simple, propel vous génère un fichier de description de formulaire adapaté à votre schema dans le dossier «lib/form». Vous remarquerez qu'à l'instar des fichiers du modèle, il existe un dossier «lib/forms/base» qui contient les classes qui seront regénérées à chaque build-all et que les fichiers de «lib/forms» vous sont destinés.
Reste maintenant dans l'action qui affiche un article de blog à initialiser notre formulaire :
public function executeShow()
{
$this->post = BlogPostPeer::retrieveByPK($this->getRequestParameter('blog_post_id'));
$this->forward404Unless($this->post);
$this->comment_form = new BlogCommentForm();
}
Bien que propel nous ait maché le travail, le résultat n'est pas forcément celui attendu :
Le champs created_at ne devrait pas non plus pouvoir être modifié car fixé par défaut par symfony au moment de la création de l'enregistrement dans la base.
Il va donc falloir configurer notre formulaire. Le but étant de: - changer le champs blog_post_id en champs hidden avec une valeur fixée par défaut
- ne pas s'occuper ni valider le champs created_at (en gros, l'enlever du formulaire).
Pour cela, il est nécessaire de récupérer le widgetSchema de notre formulaire pour le changer.
Notre action devient alors :
public function executeShow()
{
$this->post = BlogPostPeer::retrieveByPK($this->getRequestParameter('blog_post_id'));
$this->forward404Unless($this->post);
$this->comment_form = new BlogCommentForm();
$widget_schema = $this->comment_form->getWidgetSchema();
$widget_schema['blog_post_id'] = new sfWidgetFormInputHidden();
$this->comment_form->setDefault("blog_post_id", $this->getRequestParameter('id'));
unset($widget_schema['created_at']);
}Vous remarquez que nous utilisons l'implémentation array_access de l'objet widgetSchema. Nous pouvons donc accéder aux différents widgets de la même façon que les éléments d'un tableau. Notre formulaire prend alors bien la tête de ce que nous attendons :
Le formulaire passé au peigne fin par la web developer toolbar possède bien juste les champs dont nous avons besoin et notemment le blog_post_id en champs hidden.
Maintenant que ce problème est (pour l'instant) réglé, penchons nous sur la validation de ce formulaire.
Nous allons utiliser la même approche que la dernière fois le formulaire postant par défaut sur la dernière action appelée.
public function executeShow()
{
$this->comment_form = new BlogCommentForm();
$widget_schema = $this->comment_form->getWidgetSchema();
unset($widget_schema['created_at']);
if ($this->getRequest()->getMethod() == sfRequest::POST)
{
$blog_comment = $this->getRequestParameter('blog_comment');
$this->comment_form->BindAndSave($blog_comment);
$this->redirectIf($this->comment_form->isValid(), 'post/show?blog_post_id='.$blog_comment['blog_post_id']);
$this->post = BlogPostPeer::retrieveByPK($blog_comment['blog_post_id']);
}
else
{
$this->post = BlogPostPeer::retrieveByPK($this->getRequestParameter('blog_post_id'));
}
$this->forward404Unless($this->post);
$widget_schema['blog_post_id'] = new sfWidgetFormInputHidden();
$this->comment_form->setDefault("blog_post_id", $this->post->getId());
}Quelques explications :
unset($widget_schema['created_at']);Je suis obligé dès l'instanciation de mon formulaire de me débarasser de ce champs pour qu'il ne soit ni affiché ni validé. Nous testons ensuite si nous sommes dans une requête POST. Si c'est le cas alors je vais valider mon formulaire :
$this->comment_form->BindAndSave($blog_comment);Vous remarquez que pour le coup rien de plus simple : cette action :
- clean les entrées
- valide les données
- sauvegarde le nouvel objet commentaire si le formulaire est valide.
Ne me reste qu'à rediriger l'utilisateur si son formulaire est valide :
$this->redirectIf($this->comment_form->isValid(), 'post/show?blog_post_id='.$blog_comment['blog_post_id']);Si ce n'est pas le cas alors je prépare de nouveau l'environnement de mon template en utilisant la valeur passée dans le formulaire pour retrouver le BlogPost courant.
Testons notre formulaire, nous découvrerons que lorsque je rentre «bleuargh» dans le champs email, mon formulaire est néanmoins validé ... à l'arnaque, au voleur !
Que se passe-t-il, c'est tout simple, notre formulaire ignore que cette valeur est une adresse email. Se basant sur le schema.yml il sait juste que c'est un champs text qui valide «bleuargh» sans aucun soucis. Il convient de changer le validateur associé à ce champs au moment de la validation.
$validator_schema = $this->getValidatorSchema();
$validator_schema['email'] = new sfValidatorEmail();
Là encore nous utilisons l'implémentation array_access de l'objet validator_schema pour accéder à notre validateur. Rien de plus simple alors de lui préciser sfValidatorEmail.Refactorisons notre code.
Tous ce code de modification de notre formulaire peut sans problème être factorisé dans une classe héritant de BlogCommentForm. Je ne sais pas encore si utiliser «/lib/forms/BlogCommentForm.php» est une bonne idée ou non. J'ai plus plus l'intuition que ce formulaire est utile dans notre module et de laisser l'objet principal inchangé. J'ai donc opté pour utiliser le lib/ de mon module pour surcharger cet objet.
apps/frontend/modules/post/lib/commentForm.class.php
<?php
class commentForm extends BlogCommentForm
{
public function configure()
{
parent::configure();
$widget_schema = $this->getWidgetSchema();
$widget_schema['blog_post_id'] = new sfWidgetFormInputHidden();
unset($widget_schema['created_at']);
$validator_schema = $this->getValidatorSchema();
$validator_schema['email'] = new sfValidatorEmail();
}
}Voila mon action qui devient plus claire, plus lisible :
apps/frontend/modules/post/actions/actions.class.php
public function executeShow()
{
$this->comment_form = new commentForm();
if ($this->getRequest()->getMethod() == sfRequest::POST)
{
$blog_comment = $this->getRequestParameter('blog_comment');
$this->comment_form->BindAndSave($blog_comment);
$this->redirectIf($this->comment_form->isValid(), 'post/show?blog_post_id='.$blog_comment['blog_post_id']);
$this->post = BlogPostPeer::retrieveByPK($blog_comment['blog_post_id']);
}
else
{
$this->post = BlogPostPeer::retrieveByPK($this->getRequestParameter('blog_post_id'));
}
$this->forward404Unless($this->post);
$this->comment_form->setDefault("blog_post_id", $this->post->getId());
}
Publicité