symfony Forms in Action

Chapitre 3 - Les Formulaires pour les Intégrateurs HTML

About

You are currently reading "symfony Forms in Action" which is licensed under the Creative Commons Attribution-Share Alike 3.0 Unported License license.

Symfony Live 2010 Paris Conference

Chapter Content

Avant de commencer

La Template Prototype

La personnalisation de la 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'Internationalisation

L'Interaction avec le Développeur

symfony training
Be trained by symfony experts
Jul 22: Paris (1.2 + Doctrine - Français)
Aug 19: San Francisco (1.2 + Doctrine - English)
Sep 23: Paris (1.2 + Doctrine - Français)
Oct 21: Nantes (1.2 + Doctrine - Français)
Nov 18: Paris (1.2 + Doctrine - Français)
and more...

Search


powered by google
You are currently browsing "symfony Forms in Action" in French for the 1.1 version - Switch to version: - Switch to language:
Creative Commons License This work is licensed under a Creative Commons Attribution-Share Alike 3.0 Unported License.

Nous avons vu dans les Chapitre 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 la 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".

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 but de ce chapitre est d'illustrer les possibilités offertes pour personnaliser la template prototype que nous avons utilisée jusqu'à maintenant pour l'affichage (Listing 3-1).

Figure 3-1 - Le Formulaire de Contact

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 la 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 de la template, chaque champ est composé de trois éléments :

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ée 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>

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

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 :

Figure 3-3 - Décomposition du Champ email du Formulaire

Décomposition du Champ <code>email</code> du Formulaire

Chaque champ généré possède un attribut id par défaut permettant d'ajouter des styles ou des comportements JavaScript.

La personnalisation de la 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.

La template est fonctionnellement identique à la 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()

Personnalisation de l'Affichage via les Arguments de la méthode <code>renderRow()</code>

Regardons en détail les arguments passés à l'appel renderRow() pour générer le champ email :

De même pour le champ message :

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()

Structure HTML utilisée par <code>renderRow()</code> et <code>render()</code>

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 :

Figure 3-6 - Méthodes disponibles pour personnaliser chaque Champ

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 souhaitons 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

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, la template peut-être réécrite 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 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

La template actuelle 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.

Figure 3-8 - Affichage du Message d'Erreur global

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>

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 :

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]
  <?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.

La template est maintenant fonctionnellement équivalente à la 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

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 :

Une fois ce premier cycle achevé, les modifications à apporter aux règles métier et aux templates peuvent être réalisées 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 :

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 :

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.

Chapitre 4 - L'Intégration avec Propel »
« Chapitre 2 - La Validation des Formulaires

Questions & Feedback

If you find a typo or an error, please register and open a ticket.

If you need support or have a technical question, please post to the official user mailing-list.

The Sensio Labs Network

Since 1998, Sensio Labs has been promoting the Open-Source software movement by providing quality web application development, training, consulting, and supporting several large Open-Source projects.