Les formulaires
Un composant MVC
Les formulaires sont des éléments indispensables aux sites web : c’est le principal moyen par lequel les utilisateurs interagissent avec l’application.
Ils sont affichés dans des pages (couche Vue) et, une fois soumis, sont généralement utilisés pour modifier des données (couche Modèle), tout ceci étant orchestré par le contrôleur.
On retrouve donc les protagonistes de notre fameux modèle de conception MVC (cf. Architecture du framework - Le modèle de conception MVC).
Cette particularité fait du composant « Form » de Symfony non pas le plus difficile à prendre en main, mais l’un des plus complets. Aussi, pour mieux appréhender ce chapitre, il convient de maîtriser le Contrôleur et Twig.
1. Le modèle
Dans leur utilisation la plus fréquente, les formulaires permettent d’interagir avec la couche Modèle (bien que le composant puisse fonctionner avec des tableaux). Les « cibles » des formulaires sont donc des objets.
Imaginons une classe représentant un client :
<?php
namespace App\Model;
class Client
{
private $nom;
private $dateDeNaissance;
public function setNom($nom) ...
Fonctionnement du composant
1. L’objet « Form »
L’objet Form est le principal élément utilisé au niveau du contrôleur. Dans l’exemple ci-dessus, il est contenu dans la variable $form. Il représente l’ensemble des champs du formulaire, sous forme hiérarchique.
C’est le point central autour duquel tout s’articule. L’objet représentant la requête HTTP ou l’objet de la couche Modèle y est « injecté ». Les tâches de traitement classiques (telles que la gestion de la soumission, la validation ou la création de la vue du formulaire) se font également au travers de cet objet Form.
a. Soumission
L’objet Form comporte une méthode handleRequest(), prenant en paramètre l’objet Request (la requête HTTP courante). Cette méthode est capable de l’introspecter.
De cette introspection naîtra une conclusion, à savoir, une réponse à la question suivante : est-ce que l’utilisateur, lors de la requête HTTP courante, est en train de soumettre le formulaire ou est-il seulement en train de l’afficher ?
S’il est en train de le soumettre (généralement cette action est reconnaissable par le fait que la méthode de la requête HTTP courante est de type POST), les données postées sont rattachées au formulaire et l’objet de la couche Modèle est également mis à jour (dans la mesure où les données saisies par l’utilisateur sont valides, nous y reviendrons plus tard).
Par défaut, invoquer la méthode handleRequest() serait équivalent à :
if ($request->isMethod('POST')) {
$form->submit($request);
}
La méthode submit() permet de rattacher manuellement des données à un formulaire.
b. Validation
La méthode isValid() permet de savoir si les valeurs soumises par l’utilisateur sont correctes, en fonction d’un ensemble de règles (dites « contraintes ») définies par le développeur.
Ces règles peuvent être définies à la volée, c’est-à-dire durant la création du formulaire...
Les types de champs de formulaire
L’objectif de cette section du chapitre est de vous donner un aperçu des différents types de champs de formulaire et de leurs options. Cela n’est en aucun cas une liste exhaustive mais plutôt un référentiel de base pour la compréhension du fonctionnement de ces champs.
Afin de trouver la liste complète des types de champs de formulaire ainsi que leurs options, rendez-vous sur la page de documentation dédiée au composant « Form » à l’adresse : https://symfony.com/doc/6.4/reference/forms/types.html
1. L’héritage
Avant d’aborder les différents types ainsi que leurs options, il est nécessaire de comprendre l’héritage de ceux-ci.
Chaque type peut hériter d’un parent. En pratique, la quasi-totalité des types utilisés avec Symfony héritent du même type : FormType. Nous avons déjà utilisé ce type indirectement : le « nœud » principal de la hiérarchie du formulaire est par défaut de type FormType.
Au cours de cette section dédiée aux types, le parent de chacun de ceux-ci sera spécifié. Cela vous permettra de retrouver la totalité de leurs options. Si un type B a comme parent A, toutes les options de A sont alors disponibles lors de l’utilisation de B.
2. FormType
C’est un type plutôt abstrait, contenant des options de configuration assez générales. La plupart du temps, nous ne l’utilisons pas directement, mais au travers de ses nombreux sous-types.
a. label
Cette option indique le label à utiliser pour le champ. Si cette option n’est pas renseignée, le framework fait de son mieux pour « deviner » le label ; par exemple, un champ nommé date_de_naissance aura le label Date de naissance.
b. label attr
En passant un tableau associatif à cette option, vous pouvez définir les attributs HTML pour la balise <label>.
c. data
Cette option permet de modifier la valeur par défaut du champ.
Par exemple, pour préremplir un champ de type text (il hérite de form) avec une valeur donnée, il suffit de configurer le type de cette manière :
$formBuilder->add('champ', TextType::class...
Créer des formulaires réutilisables
Pour l’instant, nous avons créé des formulaires directement depuis le contrôleur, grâce au FormBuilder. Cependant, une bonne pratique consiste à extraire la configuration d’un formulaire au sein d’une classe dédiée. Cela permet une meilleure clarté du code et rend par la même occasion le formulaire réutilisable.
1. Définir un formulaire avec la classe AbstractType
Pour ce faire, nous devons créer une classe implémentant l’interface Symfony\Component\Form\FormTypeInterface. Pour simplifier cette tâche, nous pouvons nous servir de la classe Symfony\Component\Form\AbstractType. Cette classe contient différentes méthodes liées à la configuration d’un formulaire.
En réalité, et comme nous l’avons vu précédemment, ce que nous entendons par « formulaire » est simplement le nœud principal d’une certaine arborescence. À ce titre, la principale différence entre un formulaire permettant la modification d’informations sur un client et un champ de type text est que le premier est un nœud avec des enfants, tandis que le second est un nœud simple. C’est pour cette raison que pour configurer un formulaire au sein d’une classe, nous définissons un type.
Cela peut prêter à confusion mais il est très important de comprendre le point précédent, car de celui-ci découlent deux utilisations distinctes de la classe AbstractType :
-
Configurer les formulaires d’une application (inscription d’utilisateurs, passage de commandes, etc.).
-
Configurer ses propres types pour pouvoir les réutiliser dans ses applications, ou étendre les types natifs du framework pour les personnaliser. D’ailleurs, les types natifs de Symfony étendent cette même classe et vous pouvez les retrouver au sein de l’espace de noms Symfony\Component\Form\Extension\Core\Type.
L’exemple suivant contient la configuration d’un formulaire relatif aux coordonnées d’un client :
<?php
namespace App\Form\Type;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface; ...
Validation des données
1. Objectifs
Avec Symfony, la validation est un composant pouvant être utilisé de manière autonome. Cependant, en pratique, il est principalement utilisé pour la validation de données en provenance de formulaires. C’est pour cette raison que nous l’abordons au sein de ce chapitre.
Nous entendons ici la validation « côté serveur ». Il est important de ne pas la confondre avec la validation côté client en HTML5.
Les options telles que required, ou le fait d’utiliser un champ de type email, donneront lieu à une validation au niveau du navigateur, mais celle-ci ne doit en aucun cas être considérée comme fiable. C’est seulement un complément à la validation côté serveur car cela permet de réduire les interactions avec le serveur en détectant des erreurs avant la soumission.
2. La définition des contraintes de validation
Les contraintes de validation sont un ensemble de règles pouvant être appliquées à une valeur. Ces règles permettent de s’assurer que la valeur correspond à un ou plusieurs critères donnés.
Concrètement, pour un formulaire d’inscription par exemple, ces règles pourraient être les suivantes :
-
Le pseudo doit contenir entre 3 et 20 caractères.
-
L’adresse e-mail saisie doit être valide.
-
Les noms et prénoms doivent être renseignés.
-
etc.
Il existe deux méthodes pour configurer les contraintes d’un formulaire :
-
Au niveau de l’objet de la couche Modèle qui est associé au formulaire.
-
Sur les champs de formulaire en utilisant l’option constraints.
Configurons la validation d’un formulaire d’envoi d’e-mails avec les deux méthodes. Le formulaire est constitué avec les champs et règles suivants :
-
destinataire : champ de type email ; sa valeur doit être renseignée et correspondre à un e-mail valide.
-
titre : champ de type text ; sa valeur est optionnelle.
-
message : champ de type textarea, ce champ est obligatoire.
-
piece_jointe : champ optionnel de type file pour une éventuelle pièce jointe.
a. Ajout des contraintes lors de la configuration d’un formulaire...
Personnaliser le rendu - thèmes de formulaires
Le rendu du formulaire intervient au niveau de la couche Vue. Tout comme pour la gestion des templates, il peut être utilisé avec deux langages : Twig et PHP. Ici aussi, nous allons nous concentrer sur son utilisation au travers de Twig.
Il existe plusieurs approches quant à la personnalisation du rendu d’un formulaire, allant d’un contrôle direct depuis le template où il est affiché à des processus plus automatisés avec les « thèmes de formulaires ».
1. Afficher le formulaire manuellement
Pour l’instant, nous avons utilisé la méthode Twig form() sur l’objet FormView pour afficher un formulaire :
{{ form(form) }}
Cette simple fonction permet l’affichage du formulaire entier. Elle est donc très pratique mais est aussi très restrictive. En effet, elle n’offre aucune possibilité de contrôle sur le rendu final.
Le morceau de code suivant est équivalent à la fonction form() :
{{ form_start(form) }}
{{ form_label(form) }}
{{ form_errors(form) }}
{{ form_widget(form) }}
{{ form_end(form) }}
Il introduit de nouvelles fonctions Twig liées à l’affichage de formulaires : form_start(), form_label(), form_errors(), form_widget() et form_end().
a. form_start()
Cette fonction ouvre le formulaire en affichant la balise d’ouverture <form>.
Les principaux attributs contenus dans cette balise sont action et method. Le premier désigne la page vers laquelle le formulaire doit être soumis, tandis que le second spécifie la méthode HTTP à utiliser pour la requête.
Vous pouvez contrôler ces attributs de différentes manières. Il existe tout d’abord les options method et action, qui peuvent être passées au formulaire principal :
$form = $this->createForm(new RechercheType(), null, array(
'action' => $this->generateUrl('recherche'),
'method' => 'GET', ...