Stato dispose d'un ensemble de classes facilitant la mise en place des formulaires dans vos applications. Elles prennent en charge un bon nombre des tâches courantes comme :
Les classes utilisées sont de 3 types :
Un objet SForm encapsule un ensemble de champ de formulaires et un ensemble de règles de validation devant être remplies afin que le formulaire soit accepté. L'idée est donc de créer des sous-classes de SForm dans lesquelles vous pourrez définir les champs et leurs propriétés. Par exemple, pour un formulaire de contact, vous pourriez créer la classe suivante :
class ContactForm extends SForm { public function __construct(array $data = null, array $files = null) { parent::__construct($data, $files); $this->subject = new SCharField(array('max_length' => 100)); $this->message = new STextField(); $this->sender = new SEmailField(array('required' => true)); $this->cc_me = new SBooleanField(); } }
L'affichage du formulaire dans un template peut se faire de façon très simple :
<h1>Contact us</h1>
<form action="/contact" method="post">
<?= $this->form; ?>
<input type="submit" value="Submit" />
</form>
Notez bien que le formulaire ne génère que ses propres champs, il ne s'occupe pas de la balise form ou du(des) bouton(s) de soumission.
La classe SForm génère par défaut le HTML du formulaire avec chaque champ et son label dans une balise paragraph :
<h1>Contact us</h1> <form action="/contact" method="post"> <p><label for="subject">Subject</label> <input type="text" name="subject" maxlength="100" id="subject" /></p> <p><label for="message">Message</label> <textarea name="message" cols="40" rows="10" id="message"></textarea></p> <p><label for="sender">Sender</label> <input type="text" name="sender" id="sender" /></p> <p><label for="cc_me">Cc me</label> <input type="hidden" name="cc_me" value="0" /> <input type="checkbox" name="cc_me" id="cc_me" value="1" /></p> <input type="submit" value="Submit" /> </form>
Remarquez que par défaut, l'ID HTML de chaque élément du formulaire correspond à son attribut name. Vous pouvez modifier ce comportement à l'aide de la méthode set_prefix() :
class ContactForm extends SForm { public function __construct(array $data = null, array $files = null) { parent::__construct($data, $files); $this->set_prefix('contact'); ... } }
Le résultat sera que l'ID de chaque élément sera préfixé de contact_ et que les attributs name seront modifiés de façon à ce que les données soumises soient récupérées par PHP sous la forme d'un tableau (par exemple le champ subject aura pour attribut contact[subject]) :
...<input type="text" name="contact[subject]" maxlength="100" id="contact_subject" />...
Vous pouvez alors, dans votre contrôleur, récupérer les données soumises dans $this→params['contact']. Cette fonctionnalité peut par exemple être utilisée pour placer plusieurs formulaires Stato dans un même tag <form/>. Chaque formulaire aura ainsi son propre espace de nommage.
Par convention, les fichiers contenant les classes de formulaires sont placés dans le dossier app/forms. Vous devez par ailleurs ajouter un require dans le fichier du contrôleur utilisant la classe de formulaire :
require 'forms/contact_form.php'; class ContactController extends ApplicationController { .....
La validation des données soumises se fait typiquement de la façon suivante :
public function contact() { if (!$this->request->is_post()) { $this->form = new ContactForm(); } else { $this->form = new ContactForm($this->params['contact']); if ($this->form->is_valid()) { // do something with the data $this->redirect_to(array('action' => 'thanks')); return; } } }
Si le formulaire n'a pas été soumis, on crée une instance vierge pour affichage. Si il a été soumis, on crée une instance à laquelle on lie les données soumises et vérifie que ces données sont valides. Si c'est bien le cas, on utilise les données et on redirige vers une autre page, sinon le formulaire sera automatiquement réaffiché avec les données qui ont été validées ainsi que les erreurs de validation.
Vous pouvez également lier au formulaire les données soumises en les passant en argument de is_valid() plutôt que du constructeur. Cela vous permet d'écrire votre action d'une façon légèrement différente :
public function contact() { $this->form = new ContactForm(); if ($this->request->is_post()) { if ($this->form->is_valid($this->params['contact'])) { // do something with the data $this->redirect_to(array('action' => 'thanks')); return; } } }
Une fois que is_valid retourne true, cela signifie que les données soumises se conforment bien à vos règles de validation. Vous pouvez alors accéder aux données par la propriété $cleaned_data de votre objet de formulaire. Les données contenues par cette propriété ont en effet été non seulement validées, mais aussi typées (la clé cc_me correspondra ici à une valeur booléenne par exemple) et filtrées. En reprenant l'exemple précédent, voici comment vous pourriez utiliser les données :
public function contact() { $this->form = new ContactForm(); if ($this->request->is_post()) { if ($this->form->is_valid($this->params['contact'])) { // do something with the data $subject = $this->form->cleaned_data['subject']; $message = $this->form->cleaned_data['message']; $sender = $this->form->cleaned_data['sender']; $cc_me = $this->form->cleaned_data['cc_me']; $recipients = array('info@example.com'); if ($cc_me) $recipients[] = $sender; $notifier = new Notifier; $notifier->send_contact_request_notification($subject, $message, $sender, $recipients); $this->redirect_to(array('action' => 'thanks')); return; } } }
Si la validation du formulaire échoue, les messages d'erreurs correspondants sont regroupés dans la propriété $errors du formulaire qui peut être utilisée dans le template du formulaire (par défaut, le HTML généré sera une liste à puces) :
<h1>Contact us</h1>
<form action="/contact" method="post">
<? if (!empty($this->form->errors)) : ?>
<div class="form-errors">
<?= $this->form->errors; ?>
</div>
<? endif; ?>
<?= $this->form; ?>
<input type="submit" value="Submit" />
</form>
Si le HTML généré par défaut par le formulaire ne vous convient pas, vous pouvez personnaliser la présentation du formulaire dans votre template :
<form action="/contact" method="post">
<div class="form-field">
<label for="subject">E-mail subject:</label>
<?= $this->form->subject; ?>
</div>
<div class="form-field">
<label for="message">E-mail message:</label>
<?= $this->form->message; ?>
</div>
</form>
De plus, la classe SForm implémentant l'interface Iterator, vous pouvez boucler sur les champs du formulaire :
<form action="/contact" method="post">
<table>
<? foreach ($this->form as $field) : ?>
<tr>
<td><?= $field->label_tag; ?></td>
<td><?= $field; ?></td>
<td><?= $field->error; ?></td>
</tr>
<? endforeach; ?>
</table>
</form>
Dans cette boucle, $field est une instance de SBoundField, qui possède les propriétés publiques suivantes :
$field→label : le label du champ,$field→label_tag : le label du champ dans son tag <label />,$field→html_name : l'attribut HTML name du champ,$field→help_text : le texte d'aide assigné au champ,$field→error : l'erreur de validation éventuelle du champ,$field→is_hidden : si le champ est un champ caché ou non.
Lorsque vous créez un template de présentation du formulaire, il est plus que probable que vous souhaitiez traiter les champs cachés différemment des autres, ne serait-ce que parce que vous ne souhaitez pas afficher les erreurs correspondant à ces champs. Stato propose 2 méthodes qui vous permettent de boucler sur les champs visibles et cachés de manière indépendante : visible_fields() et hidden_fields() :
<h1>Contact us</h1>
<form action="/contact" method="post">
<? foreach ($this->form->hidden_fields() as $field) : ?>
<?= $field; ?>
<? endforeach; ?>
<? foreach ($this->form->visible_fields() as $field) : ?>
<div class="form-field">
<?= $field->label_tag; ?>
<?= $field; ?>
<?= $field->error; ?>
</div>
<? endforeach; ?>
<input type="submit" value="Submit" />
</form>