- Avant de commencer
- La Template Prototype
- La personnalisation du Template prototype
- La Personnalisation de l'Affichage
- L'Utilisation de la méthode
renderRow()
sur un champ - L'Utilisation de la méthode
render()
sur un champ - L'Utilisation de la méthode
renderLabel()
sur un champ - L'Utilisation de la méthode
renderError()
sur un champ - La personnalisation fine des messages d'erreurs
- La gestion des champs cachés
- La Gestion des erreurs globales
- L'Utilisation de la méthode
- L'Internationalisation
- L'Interaction avec le Développeur
Nous avons vu dans les Chapitres 1 et 2 comment créer des formulaires en définissant des widgets et en y attachant des règles de validation. Pour les afficher, nous avons toujours utilisé l'instruction <?php echo $form ?>
qui permet au développeur de coder la logique applicative sans se préoccuper du rendu final. En effet, il n'est pas nécessaire de changer le Template à chaque modification d'un champ (nom, widget, ...) ou même lors de l'ajout de nouveaux champs. Cette instruction est donc très adaptée à la phase de prototypage et de développement initial où le développeur peut se concentrer sur le modèle et la logique métier associée.
Une fois le modèle de données stabilisé et la charte graphique définie, l'intégrateur HTML peut alors prendre le relai pour mettre en forme les différents formulaires de l'application.
Avant de commencer la lecture de ce chapitre, nous vous conseillons de bien connaître le système de templating de symfony. Pour cela, vous pouvez lire le chapitre Inside the View Layer du livre "The Definitive Guide to symfony".
note
Le système de formulaire de symfony est construit selon le modèle MVC. Ce modèle permet de séparer les rôles des membres d'une équipe de développement : le rôle du développeur est de créer les formulaires et de gérer leurs cycles de vie. Le rôle de l'intégrateur HTML est de mettre en page ces formulaires. Attention, cette séparation des rôles ne remplace bien évidemment pas le dialogue au sein de l'équipe projet.
Avant de commencer
Nous allons reprendre le formulaire de contact développé aux Chapitres 1 et 2 (Figure 3-1). Pour les intégrateurs HTML qui ne liront que ce chapitre, voici un rappel des éléments techniques nécessaires à sa compréhension :
le formulaire possède quatre champs :
name
,email
,subject
etmessage
.le formulaire est géré par le module
contact
.l'action
index
passe au Template une variableform
qui représente le formulaire.
Le but de ce chapitre est d'illustrer les possibilités offertes pour personnaliser le Template prototype que nous avons utilisé jusqu'à maintenant pour l'affichage (Listing 3-1).
Figure 3-1 - Le Formulaire de Contact
Listing 3-1 - La Template Prototype permettant d'afficher le Formulaire de Contact
// apps/frontend/modules/contact/templates/indexSuccess.php <form action="<?php echo url_for('contact/index') ?>" method="POST"> <table> <?php echo $form ?> <tr> <td colspan="2"> <input type="submit" /> </td> </tr> </table> </form>
La Template Prototype
Dans le Template prototype, vous remarquez l'utilisation de l'instruction <?php echo $form ?>
qui permet d'afficher automatiquement le contenu du formulaire.
Un formulaire est composé de champs. Au niveau du Template, chaque champ est composé de trois éléments :
le label
le tag de formulaire
les éventuels messages d'erreurs
L'instruction <?php echo $form ?>
génère automatiquement l'ensemble de ces éléments comme le montre le Listing 3-2 dans le cas d'une soumission invalide.
Listing 3-2 - Template généré en cas de Soumission invalide
<form action="/frontend_dev.php/contact" method="POST"> <table> <tr> <th><label for="contact_name">Name</label></th> <td><input type="text" name="contact[name]" id="contact_name" /></td> </tr> <tr> <th><label for="contact_email">Email</label></th> <td> <ul class="error_list"> <li>Cette adresse email est invalide.</li> </ul> <input type="text" name="contact[email]" value="fabien" id="contact_email" /> </td> </tr> <tr> <th><label for="contact_subject">Subject</label></th> <td> <select name="contact[subject]" id="contact_subject"> <option value="0" selected="selected">Subject A</option> <option value="1">Subject B</option> <option value="2">Subject C</option> </select> </td> </tr> <tr> <th><label for="contact_message">Message</label></th> <td> <ul class="error_list"> <li>Le message "foo" est trop court. Il faut au moins 4 caractères.</li> </ul> <textarea rows="4" cols="30" name="contact[message]" id="contact_message">foo</textarea> </td> </tr> <tr> <td colspan="2"> <input type="submit" /> </td> </tr> </table> </form>
tip
Il existe un raccourci supplémentaire pour générer le tag form d'ouverture du formulaire : echo $form->renderFormTag(url_for('contact/index'))
.
Il permet également de passer un nombre quelconque d'attributs supplémentaires à la balise form plus facilement en fournissant un tableau.
L'inconvénient de l'utilisation de ce raccourci est que les outils de conception auront plus de mal à détecter le formulaire correctement.
Décomposons le code généré. La Figure 3-2 souligne les lignes <tr>
générées pour chaque champ.
Figure 3-2 - Décomposition du Formulaire par Champ
Pour chaque champ, trois morceaux de code HTML ont été générés (Figure 3-3), correspondant aux trois éléments de chaque champ. Voici le code HTML généré pour le champ email
:
le label
<label for="contact_email">Email</label>
le tag de formulaire
<input type="text" name="contact[email]" value="fabien" id="contact_email" />
les messages d'erreurs
<ul class="error_list"> <li>Cette adresse email est invalide.</li> </ul>
Figure 3-3 - Décomposition du Champ email
du Formulaire
tip
Chaque champ généré possède un attribut id
par défaut permettant d'ajouter des styles ou des comportements JavaScript.
La personnalisation du Template prototype
Pour des formulaires simples comme le formulaire de contact, l'utilisation de l'instruction <?php echo $form ?>
peut s'avérer suffisante. Cette instruction est en fait un raccourci pour l'instruction <?php echo $form->render() ?>
.
L'utilisation explicite de la méthode render()
permet de passer des attributs HTML pour chaque champ en argument. Le Listing 3-3 montre comment ajouter une classe au champ email
.
Listing 3-3 - Personnalisation des Attributs HTML via la Méthode render()
<?php echo $form->render(array('email' => array('class' => 'email'))) ?> // HTML généré <input type="text" name="contact[email]" value="" id="contact_email" class="email" />
Cela permet de changer rapidement les styles du formulaire mais n'offre pas une très grande souplesse quant à l'agencement des champs sur la page.
La Personnalisation de l'Affichage
Au-delà de la personnalisation globale offerte par la méthode render()
, voyons maintenant comment décomposer l'affichage de chaque champ afin de disposer d'une plus grande souplesse d'action.
L'Utilisation de la méthode renderRow()
sur un champ
La première possibilité est de générer chaque champ de façon individuelle. L'instruction <?php echo $form ?>
est en fait équivalente à quatre appels consécutifs à la méthode renderRow()
sur le formulaire comme l'illustre le Listing 3-4.
Listing 3-3 - Utilisation de renderRow()
<form action="<?php echo url_for('contact/index') ?>" method="POST"> <table> <?php echo $form['name']->renderRow() ?> <?php echo $form['email']->renderRow() ?> <?php echo $form['subject']->renderRow() ?> <?php echo $form['message']->renderRow() ?> <tr> <td colspan="2"> <input type="submit" /> </td> </tr> </table> </form>
Nous accédons ici à chaque champ en manipulant l'object form
comme si c'était un tableau PHP. Le champ email
est donc accessible via $form['email']
. La méthode renderRow()
permet d'afficher le champ sous forme d'une ligne d'un tableau HTML. L'expression $form['email']->renderRow()
permet donc de générer la ligne représentant le champ email
. En répétant le même type de code pour les trois autres champs subject
, email
et message
, nous complétons l'affichage du formulaire.
Le Template est fonctionnellement identique au Template de départ. Si l'affichage reste le même, la personnalisation est plus facile avec la méthode renderRow()
car elle prend deux arguments : un tableau d'attributs HTML et le nom du label. Le Listing 3-5 met en oeuvre ces deux arguments pour personnaliser le formulaire (le rendu final est visible Figure 3-4).
Listing 3-5 - Utilisation des Arguments de la Méthode renderRow()
pour Personnaliser l'Affichage
<form action="<?php echo url_for('contact/index') ?>" method="POST"> <table> <?php echo $form['name']->renderRow() ?> <?php echo $form['email']->renderRow(array('class' => 'email')) ?> <?php echo $form['subject']->renderRow() ?> <?php echo $form['message']->renderRow(array(), 'Votre Message') ?> <tr> <td colspan="2"> <input type="submit" /> </td> </tr> </table> </form>
Figure 3-4 - Personnalisation de l'Affichage via les Arguments de la méthode renderRow()
Regardons en détail les arguments passés à l'appel renderRow()
pour générer le champ email
:
array('class' => 'email')
permet d'ajouter la classeemail
au tag<input>
De même pour le champ message
:
array()
indique qu'on ne souhaite pas ajouter d'attributs HTML au tag<textarea>
'Votre message'
remplace le nom du label généré par défaut
Tous les arguments de la méthode renderRow()
étant facultatifs, il est possible de ne passer aucun argument comme nous l'avons fait pour les champs name
et subject
.
Même si l'utilisation de la méthode renderRow()
permet de personnaliser les éléments de chaque champ, la structure englobant ces éléments est figée et le rendu final est donc contraint comme l'illustre la Figure 3-5.
Figure 3-5 - Structure HTML utilisée par renderRow()
et render()
Pour s'affranchir de cette structure, chaque champ possède des méthodes permettant de générer les éléments le composant individuellement, comme l'illustre la Figure 3-6 :
renderLabel()
: le label (le tag<label>
associé au champ)render()
: le tag du champ lui-même (par exemple le tag<input>
)renderError()
: les messages d'erreurs (sous la forme d'une liste<ul class="error_list">
)
Figure 3-6 - Méthodes disponibles pour personnaliser chaque Champ
La suite de ce chapitre va détailler l'utilisation de chacune de ces méthodes.
L'Utilisation de la méthode render()
sur un champ
Imaginons que nous souhaitions afficher le formulaire sur deux colonnes. Comme illustré sur la Figure 3-7, les champs name
et email
sont sur la même ligne, alors que les champs subject
et message
sont sur leur propre ligne.
Figure 3-7 - Mise en Page du Formulaire sur plusieurs Colonnes
Pour cela, il est nécessaire de pouvoir générer chaque élément d'un champ individuellement. Nous avons déjà vu que pour accéder à un champ, on pouvait utiliser l'objet formulaire form
comme un tableau associatif avec comme clé le nom du champ. Par exemple, le champ email
est accessible par $form['email']
. Le Listing 3-6 montre une implémentation du formulaire sur deux colonnes.
Listing 3-6 - Personnalisation de l'Affichage sous forme de deux Colonnes
<form action="<?php echo url_for('contact/index') ?>" method="POST"> <table> <tr> <th>Name:</th> <td><?php echo $form['name']->render() ?></td> <th>Email:</th> <td><?php echo $form['email']->render() ?></td> </tr> <tr> <th>Subject:</th> <td colspan="3"><?php echo $form['subject']->render() ?></td> </tr> <tr> <th>Message:</th> <td colspan="3"><?php echo $form['message']->render() ?></td> </tr> <tr> <td colspan="4"> <input type="submit" /> </td> </tr> </table> </form>
De même que pour l'affichage complet du formulaire avec <?php echo $form ?>
, l'utilisation explicite de la méthode render()
sur un champ n'est pas nécessaire, le Template peut-être réécrit comme dans le Listing 3-7.
Listing 3-7 - Simplification de la Personnalisation de l'Affichage sous forme de deux Colonnes
<form action="<?php echo url_for('contact/index') ?>" method="POST"> <table> <tr> <th>Name:</th> <td><?php echo $form['name'] ?></td> <th>Email:</th> <td><?php echo $form['email'] ?></td> </tr> <tr> <th>Subject:</th> <td colspan="3"><?php echo $form['subject'] ?></td> </tr> <tr> <th>Message:</th> <td colspan="3"><?php echo $form['message'] ?></td> </tr> <tr> <td colspan="4"> <input type="submit" /> </td> </tr> </table> </form>
Comme pour le formulaire, chaque champ peut être personnalisé en passant à la méthode render()
un tableau d'attributs HTML. Le Listing 3-8 montre comment modifier la classe du champ email
.
Listing 3-8 - Modification des Attributs HTML grâce à la Méthode render()
<?php echo $form['email']->render(array('class' => 'email')) ?> // HTML généré <input type="text" name="contact[email]" class="email" id="contact_email" />
L'Utilisation de la méthode renderLabel()
sur un champ
Dans la personnalisation du paragraphe précédent, nous n'avons pas généré de labels. Le Listing 3-9 utilise la méthode renderLabel()
pour générer le label de chaque champ.
Listing 3-9 - Utilisation de renderLabel()
<form action="<?php echo url_for('contact/index') ?>" method="POST"> <table> <tr> <th><?php echo $form['name']->renderLabel() ?>:</th> <td><?php echo $form['name'] ?></td> <th><?php echo $form['email']->renderLabel() ?>:</th> <td><?php echo $form['email'] ?></td> </tr> <tr> <th><?php echo $form['subject']->renderLabel() ?>:</th> <td colspan="3"><?php echo $form['subject'] ?></td> </tr> <tr> <th><?php echo $form['message']->renderLabel() ?>:</th> <td colspan="3"><?php echo $form['message'] ?></td> </tr> <tr> <td colspan="4"> <input type="submit" /> </td> </tr> </table> </form>
Il est possible de remplacer le nom du label généré automatiquement à partir du nom du champ en passant un argument à la méthode renderLabel()
comme dans le Listing 3-10.
Listing 3-10 - Modification du Nom du Label
<?php echo $form['message']->renderLabel('Votre Message') ?> // HTML généré <label for="contact_message">Votre Message</label>
Mais quelle est l'utilité de la méthode renderLabel()
si on passe le nom du label en
paramètre ? Pourquoi ne pas coder directement en HTML le tag label
? Parce qu'en
générant le tag label
, la méthode renderLabel()
ajoute automatiquement un attribut for
ayant comme valeur l'identifiant du champ lié (id
). Cela permet de garantir l'accessibilité
du champ ; en cliquant sur le label, le champ correspondant obtient automatiquement le
focus :
<label for="contact_email">Email</label> <input type="text" name="contact[email]" id="contact_email" />
De plus, le deuxième argument de la méthode renderLabel()
permet de spécifier des attributs HTML :
<?php echo $form['send_notification']->renderLabel(null, array('class' => 'inline')) ?> // HTML généré <label for="contact_send_notification" class="inline">Send notification</label>
Dans cet exemple, le premier argument passé est null
pour conserver la génération automatique du texte du label.
L'Utilisation de la méthode renderError()
sur un champ
Le Template actuel n'affiche pas les messages d'erreurs. Le Listing 3-11 les rétablit en utilisant la méthode renderError()
.
Listing 3-11 - Affichage des Messages d'Erreurs avec la méthode renderError()
<form action="<?php echo url_for('contact/index') ?>" method="POST"> <table> <tr> <th><?php echo $form['name']->renderLabel() ?>:</th> <td> <?php echo $form['name']->renderError() ?> <?php echo $form['name'] ?> </td> <th><?php echo $form['email']->renderLabel() ?>:</th> <td> <?php echo $form['email']->renderError() ?> <?php echo $form['email'] ?> </td> </tr> <tr> <th><?php echo $form['subject']->renderLabel() ?>:</th> <td colspan="3"> <?php echo $form['subject']->renderError() ?> <?php echo $form['subject'] ?> </td> </tr> <tr> <th><?php echo $form['message']->renderLabel() ?>:</th> <td colspan="3"> <?php echo $form['message']->renderError() ?> <?php echo $form['message'] ?> </td> </tr> <tr> <td colspan="4"> <input type="submit" /> </td> </tr> </table> </form>
La personnalisation fine des messages d'erreurs
L'utilisation de la méthode renderError()
permet de générer la liste des erreurs associées à un champ. Cette méthode ne génère du code que si le champ présente au moins une erreur. Par défaut, la liste est générée sous la forme d'une liste non ordonnée (<ul>
).
Même si ce comportement convient pour la plupart des cas d'utilisation courants, les méthodes hasError()
et getError()
permettent d'accéder directement aux erreurs. Le Listing 3-12 montre la personnalisation des messages d'erreurs pour le champ email
.
Listing 3-12 - Accès aux Messages d'Erreurs
<?php if ($form['email']->hasError()): ?> <ul class="error_list"> <?php foreach ($form['email']->getError() as $error): ?> <li><?php echo $error ?></li> <?php endforeach; ?> </ul> <?php endif; ?>
Dans notre exemple, le code généré est strictement le même que le code généré par défaut.
La gestion des champs cachés
Imaginons maintenant que le formulaire possède un champ caché obligatoire referer
. Ce champ permet de connaître la provenance de l'internaute lors de l'accès au formulaire. L'instruction <?php echo $form ?>
génère le code HTML des champs cachés et l'ajoute lors de la génération du dernier champ visible comme illustré par le code du Listing 3-13.
Listing 3-13 - Génération du Code pour les Champs cachés
<tr> <th><label for="contact_message">Message</label></th> <td> <textarea rows="4" cols="30" name="contact[message]" id="contact_message"></textarea> <input type="hidden" name="contact[referer]" id="contact_referer" /> </td> </tr>
Comme vous pouvez le constater sur le code généré pour le champ caché referer
, seul l'élément tag est présent. Il est logique ne pas générer de label mais qu'en est-il des éventuelles erreurs pouvant survenir pour ce champ ? Même si le champ est caché, il peut être corrompu dans le processus soit de façon intentionnelle, soit parce qu'une erreur s'est glissée dans le code. Ces erreurs ne sont pas directement liées au champ referer
mais sont agrégées avec les erreurs globales. Nous verrons dans le Chapitre 5 que la notion d'erreurs globales recouvre également d'autres cas. La Figure 3-8 illustre l'affichage du message d'erreur sur le champ referer
et le Listing 3-14 montre le code généré pour ces erreurs.
Vous pouvez rendre tous les champs masqués en une fois (y compris les CSRF) en utilisant la méthode renderHiddenFields().
Figure 3-8 - Affichage du Message d'Erreur global
Listing 3-14 - Génération des Messages d'Erreurs globaux
<tr> <td colspan="2"> <ul class="error_list"> <li>Referer: Required.</li> </ul> </td> </tr>
caution
Lorsque vous personnalisez un formulaire, n'oubliez pas d'intégrer les champs cachés et les messages d'erreurs globaux.
La Gestion des erreurs globales
Les erreurs liées à un formulaire peuvent être de trois types :
- les erreurs liées à un champ particulier
- les erreurs globales
- les erreurs liées à des champs cachés ou à des champs non affichés sur le formulaire. Ces erreurs sont agrégées avec les erreurs globales
Nous avons déjà vu comment intégrer les messages d'erreurs liés à un champ, le Listing 3-15 montre l'intégration des messages d'erreurs globaux.
Listing 3-15 - Intégration des Messages d'Erreurs globaux
<form action="<?php echo url_for('contact/index') ?>" method="POST"> <table> <tr> <td colspan="4"> <?php echo $form->renderGlobalErrors() ?> </td> </tr> // ... </table>
L'appel à la méthode renderGlobalErrors()
permet d'afficher la liste des erreurs globales. Il est également possible d'accéder aux erreurs globales via les méthodes hasGlobalErrors()
et getGlobalErrors()
comme le montre le Listing 3-16.
Listing 3-16 - Utilisation de hasGlobalErrors()
et getGlobalErrors()
pour personnaliser l'Affichage des Erreurs Globales
<?php if ($form->hasGlobalErrors()): ?> <tr> <td colspan="4"> <ul class="error_list"> <?php foreach ($form->getGlobalErrors() as $name => $error): ?> <li><?php echo $name.': '.$error ?></li> <?php endforeach; ?> </ul> </td> </tr> <?php endif; ?>
Vous remarquez dans le Listing 3-16 que chaque erreur possède un nom (name
) et un message (error
). Le nom est vide s'il s'agit d'une erreur globale. Pour les erreurs liées à des champs cachés ou à des champs non affichés, le nom est le label du champ.
Le Template est maintenant fonctionnellement équivalent au Template de départ (Figure 3-8) mais nous pouvons désormais personnaliser l'affichage du formulaire.
Figure 3-8 - Formulaire personnalisé grâce aux Méthodes sur les Champs
L'Internationalisation
Tous les éléments composant les champs des formulaires, et notamment les labels et les messages d'erreurs sont automatiquement gérés par le système d'internationalisation de symfony. Cela signifie que l'intégrateur HTML n'a aucune action particulière à mettre en oeuvre pour internationaliser les formulaires, même lorsqu'il passe de façon explicite un label à la méthode renderLabel()
. La traduction est automatiquement prise en compte. Pour plus d'information sur l'internationalisation des formulaires, veuillez vous référer au Chapitre 9.
L'Interaction avec le Développeur
Finissons ce chapitre par la description d'un scénario typique de développement d'un formulaire avec symfony :
L'équipe de développement commence par implémenter la classe de formulaire et l'action correspondante. Le Template se résume alors à l'utilisation de l'instruction de prototypage
<?php echo $form ?>
.Parallèlement, les créatifs définissent la charte graphique et les règles d'affichage liées aux formulaires : structure générale, règles d'affichage des messages d'erreurs, ...
Une fois la logique métier implémentée et la charte graphique validée, l'équipe d'intégration peut alors modifier les Templates des formulaires pour les mettre en page. Pour cela, elle a uniquement besoin de connaître le nom des champs et l'action qui permet de gérer le cycle de vie du formulaire.
Une fois ce premier cycle achevé, les modifications à apporter aux règles métier et aux Templates peuvent être réalisés en parallèle.
Sans aucune incidence sur les Templates, et donc sans intervention de l'équipe d'intégration, l'équipe de développement peut :
- modifier les widgets utilisés
- personnaliser les messages d'erreurs
- modifier, ajouter ou supprimer des règles de validation
De la même manière, l'équipe d'intégration est libre d'effectuer toutes les modifications ergonomiques et de design sans avoir recours à l'équipe de développement.
Par contre, les actions suivantes nécessitent une coordination des équipes :
- renommage d'un champ
- ajout ou suppression d'un champ
Cette concertation est logique puisqu'elle a des répercussions à la fois sur les règles métiers mais également sur l'affichage du formulaire. Comme nous l'avons indiqué en préambule de ce chapitre, même si le système de formulaire sépare bien les tâches, rien ne vaut la concertation entre les équipes.
This work is licensed under the Creative Commons Attribution-Share Alike 3.0 Unported License license.