Dans cet article nous verrons comment implémenter un formulaire vraiment dynamique. Mais au faite, connaissez-vous la différence entre un formulaire dynamique et un formulaire vraiment dynamique ? La réponse vous attend juste en dessous.
Et pourquoi s’y intéresser ? Les avantages sont nombreux. Mais, pour moi, le principal avantage est de générer et afficher des champs dynamiques sans avoir à anticiper les données que l’utilisateur saisira dans son formulaire. Cet avantage offre à l’utilisateur plus de flexibilité pour ajouter toutes les informations nécessaires au bon fonctionnement de votre application. Prêt ? C’est parti…
Si vous êtes un développeur Symfony, vous n’êtes pas sans savoir qu’il est possible d’afficher dynamiquement un champ en fonction des valeurs prédéfinies d’un champ cible.
Voici d’ailleurs un exemple tiré de la documentation officielle de Symfony UX Component.
Je ne vais pas m’attarder là-dessus, car l’objectif ici est d’aller plus loin. Grâce à la librairie symfonycasts/dynamic-forms, nous pouvons afficher un champ en fonction d’une valeur choisie. Mais cela suppose que nous devons connaître à l’avance les valeurs que nous avons définies dans le champ parent. Ce qui casse un peu le côté dynamique du formulaire.
Dans ce que nous souhaitons réaliser ici, c’est plutôt d’afficher, en fonction d’un champ dont les valeurs ne sont pas forcément connues à l’avance, des champs enfants qui permettront de modifier des variables associées à ce formulaire.
Lorsque j’ai développé cette fonctionnalité, j’ai d’abord cherché des exemples déjà existants. L’idée étant de ne pas réinventer la roue. Mais je n’ai rien trouvé. J’ai donc décidé de vous proposer un exemple fonctionnel et optimisé d’un formulaire vraiment dynamique.
Prenons un exemple concret :
Je souhaite générer un document en me basant sur un gabarit (ou template). Chaque gabarit défini ses propres variables : un titre, un sous-titre, un paragraphe libre, une image de signature, etc. Mais tous les templates ne sont pas composés des mêmes variables, et le formulaire de création de document doit donc s’adapter dynamiquement.
Notre objectif sera d’afficher uniquement les champs correspondant aux variables du gabarit sélectionné, sans recharger la page, tout en gardant un traitement propre côté serveur.
Autrement dit dans un formulaire de création d’un document, nous allons offrir à l’utilisateur, selon le gabarit choisi, la possibilité de créer les variables associées à ce template.
Voilà, après ces explications un peu casse-tête, je vous propose de passer au vif du sujet... c’est-à-dire à la conception technique.
Pour réaliser un formulaire dynamique, il faudra installer, au préalable, la librairie suivante : symfonycasts/dynamic-forms. Je vous laisse consulter la documentation d’installation, je ne vais pas tout vous expliquer tout de même.
Une fois l’installation réalisée, nous allons pouvoir créer notre formulaire qui contiendra tout ce qu’il nous faut pour le bon fonctionnement de notre formulaire dynamique. Pour ceci nous allons également utiliser Stimulus pour gérer l’affichage dynamique des champs sur le navigateur. Et évidemment, comme nous faisons du Symfony, nous utiliserons Twig pour les templates HTML.
Ce code crée un formulaire Symfony pour l’entité DocumentTemplate (le modèle de document), avec un comportement dynamique basé sur le champ pageTemplate (le gabarit).
Ici, l’objectif est d’afficher uniquement les champs correspondant aux variables définies par le gabarit sélectionné, et de s’assurer qu’à la soumission, seules ces variables soient traitées côté serveur. Procédons par étapes
> Étape 1 – Récupération des données
Cette première étape consiste à extraire les données nécessaires depuis le formulaire et ses options, dans le but de préparer la suite du traitement.
$documentTemplate = $builder->getData(); |
$allVariables = $options['all_variables']; |
> Étape 2 – Ajout du champ pageTemplate avec des attributs dynamiques
Dans cette étape, nous allons configurer un champ avec des attributs personnalisés en fonction des données associées à chaque option.
->add('pageTemplate', EntityType::class, [ ... 'choice_attr' => function (PageTemplate $pageTemplate) { $variables = $pageTemplate->getPageTemplateVariables()->toArray(); $codes = array_map(static fn($v) => $v->getCode(), $variables); return ['data-variables' => json_encode($codes, JSON_THROW_ON_ERROR)]; } ]) |
Cet attribut sera ensuite utilisé en JavaScript pour afficher dynamiquement les bons champs côté client.
> Étape 3 – Ajout des champs dynamiques (variables)
Cette étape consiste à ajouter tous les champs potentiels liés aux variables, en amont du filtrage dynamique côté client.
foreach ($allVariables as $variable) { ... switch ($type) { ... // Ajoute le champ avec le bon type (TextArea, Fichier, Texte) } } |
> Étape 4 – Lien de dépendance avec DynamicFormBuilder
Dans cette quatrième étape, nous établissons une dépendance logique pour permettre à l’interface de réagir dynamiquement aux changements de sélection.
$builder->addDependent( 'variables', 'pageTemplate', function (...) { ... } ); |
> Étape 5 – Nettoyage dynamique avant soumission (PRE_SUBMIT)
Et dans cette dernière étape, nous épurons dynamiquement les champs du formulaire selon le gabarit sélectionné, juste avant la validation.
$builder->addEventListener(FormEvents::PRE_SUBMIT, function (FormEvent $event) { ... foreach ($form->all() as $name => $child) { ... if (!in_array($name, $validCodes)) { $form->remove($name); } } }); |
Ce code permet de gérer dynamiquement des formulaires Symfony adaptables à différents templates, tout en garantissant performance, maintenabilité et compatibilité avec une logique de rendu côté client. Il présente notamment les avantages suivants :
Pour ajouter ces champs dynamiques, j’ai testé plusieurs manières. De l’AJAX pour construire le HTML correspondant ? Mais, cette solution ne fonctionne pas car je ne peux pas ajouter les champs dynamiquement côté serveur et garder une bonne validation du formulaire. C’est pour cette raison que j’ai préféré tous les ajouter dès le départ, puis supprimer ceux qui sont inutiles. Ce qui permet de conserver la validation native de Symfony, sans erreurs ni contournements.
Maintenant, nous allons passer au JavaScript. Sans lui, tous les champs s’afficheraient simultanément à l’écran. Nous devons donc déterminer quels champs afficher dynamiquement. Le JavaScript se chargera de n’afficher que les champs pertinents, en fonction du template choisi.
Le contrôleur Stimulus gère l’affichage des champs « variables » selon le page template choisi :
> Déclaration des cibles
Nous déclarons quelques cibles sur lequel notre Javascript va pouvoir se baser.
> Méthode connect()
Cette méthode est appelée automatiquement à l’initialisation du contrôleur.
> Méthode updateVisibleFields()
Cette méthode constitue le cœur du comportement dynamique.
> Résultat
Ce mécanisme assure un affichage du formulaire en temps réel, adapté au template sélectionné, sans recharger de la page et sans avoir à recréer ni manipuler les champs côté serveur.
Et pour finir le HTML/Twig correspondant 😉
Pas de panique, je vais vous expliquer.
> Objectif général
Ce template Twig génère un formulaire Symfony dans lequel certains champs s’afffichent dynamiquement, toujours selon le gabarit sélectionné, grâce à la liaison avec le contrôleur Stimulus.
> Démarrage et fin du formulaire
Cette section initialise le formulaire et prépare le terrain pour le fonctionnement dynamique des champs dépendants.
> Champ « pageTemplate »
Ce champ permet à l’utilisateur de choisir un gabarit, tout en étant connecté au système de gestion dynamique du formulaire.
> Champs dynamiques « variables »
Cette section regroupe tous les champs variables, prêts à être affichés ou masqués dynamiquement par le JavaScript.
Cette organisation donne au JavaScript la possibilité d’identifier facilement les champs dynamiques et de contrôler leur affichage en fonction du template sélectionné, rendant ainsi le formulaire adaptatif et fluide pour l’utilisateur.
Au final, la combinaison de ces trois bouts de code (Symfony, Stimulus et Twig) aboutit à un formulaire complètement dynamique.
Oui vous avez bien vu, tous les champs sont initialement affichés et ensuite masqué par le JavaScript. Mais grâce à la partie PHP, nous pouvons nous assurer du bon fonctionnement du formulaire. Les validations côté serveur sont parfaitement fonctionnelles, et il n’y a aucun risque que l’utilisateur saisisse n’importe quoi.
Tout au long de cet article, nous avons vu comment construire un formulaire vraiment dynamique. Je rappelle que l’utilité de ce type de formulaire est d’afficher des champs sans connaître/prévoir à l’avance le choix ou les réponses de l’utilisateur.
Maintenant, à vous de jouer ! J’espère que tout se passera bien...
Et si vous avez encore un peu de courage, je vous conseille de lire cet article : « Symfony UX : à la rencontre des Twig Components ».
Crédit photo : Rneaw