Les formulaires sont très utilisés dans le monde du web. Ils permettent de récupérer des informations, ajouter des données, demander à l’utilisateur des renseignements, etc. Il est donc essentiel de savoir s’en servir. Comprendre comment fonctionne un formulaire Symfony est donc tout aussi essentiel.
En PHP, les données sont récupérer grâce aux variables $_POST et éventuellement $_GET. Mais cette pratique est différente pour les formulaires Symfony.
Le fonctionnement des formulaires Symfony est assez différent de l’utilisation des formulaires « classiques » HTML et PHP. Mais ils ont l’avantage d’être très puissant, notamment grâce au fait qu’ils sont dissociés de la vue (HTML) et donc facilement réutilisable ailleurs.
Pour t’aider à mieux comprendre leur fonctionnement, je t’ai fait un petit tutoriel.
Nous allons voir dans cet article comment créer un formulaire symfony, les fameux « Form Type ».

Comment fonctionne un formulaire Symfony ?
Avant de commencer, rappelons le fonctionnement d’un formulaire classique.
Un formulaire est un ensemble de champs de différents types (texte, liste déroulante, bouton radio, check box) qui permettent à l’utilisateur de rentrer des données. Ces champs se trouvent à l’intérieur d’une balise « form ». Les données sont généralement enregistrées en base de données.
Le formulaire peut être soumis de différentes façons : GET, POST, PUT, DELETE. C’est ce que l’on appelle des méthodes (attribut « method » de la balise « form »)
Il a aussi une action. C’est l’URL sur laquelle seront envoyées les données du formulaire (balise « action ».)
Les formulaires avec Symfony
Avec Symfony, les formulaires vont permettre de créer et/ou modifier une entité. On dit qu’ils sont « mappés » à une entité. Dans chaque formulaire que l’on va créer, on va renseigner l’entité que ce formulaire va modifier.
Bien sûr, il est possible de ne pas mapper le formulaire et de l’utiliser uniquement pour récupérer des données sans les enregistrer en base de données. Dans ce cas, on ne spécifiera pas d’entité dans le formulaire.
Les formulaires Symfony sont des classes PHP. Le nom des classes doit se terminer par « Type ». Ex : UserType.
Formulaire rattaché à une entité (ou pas)
Comme je l’ai dit précédemment, un formulaire est rattaché à une entité.
Pourquoi ?
Parce que dans 90% des cas, lorsque l’on créé un formulaire c’est pour récupérer des données qui seront stockées en base de données. Et si elles sont stockées en base de données, c’est qu’il y a forcément une entité qui représente la table dans laquelle elles seront stockées.
Le formulaire sera donc mappé à cette entité.
Nous allons prendre le cas d’un formulaire dans lequel un utilisateur doit rentrer des informations personnelles (nom, prénom, email)
Imaginons que nous ayons l’entité « User » suivante :
<?php namespace App\Entity; use App\Repository\UserRepository; use Doctrine\ORM\Mapping as ORM; /** * @ORM\Entity(repositoryClass=UserRepository::class) * @ORM\Table(name="`user`") */ class User { /** * @ORM\Id * @ORM\GeneratedValue * @ORM\Column(type="integer") */ private $id; /** * @ORM\Column(type="string", length=255) */ private $nom; /** * @ORM\Column(type="string", length=255) */ private $prenom; /** * @ORM\Column(type="string", length=255) */ private $email; public function getId(): ?int { return $this->id; } public function getNom(): ?string { return $this->nom; } public function setNom(string $nom): self { $this->nom = $nom; return $this; } public function getPrenom(): ?string { return $this->prenom; } public function setPrenom(string $prenom): self { $this->prenom = $prenom; return $this; } public function getEmail(): ?string { return $this->email; } public function setEmail(string $email): self { $this->email = $email; return $this; } }
Nous allons créer le formulaire qui vas avec. Pour cela, Symfony nous fournit une commande permettant de créer des classes de formulaire :
symfony console make:form



Par convention, les noms de classes des formulaires doivent terminer par « Type ».
On va donc le nommer « UserType ».
La commande nous demande ensuite sur quelle entié on veut mapper notre formulaire. On lui indique donc : « User »
Une classe « UserType » vient de se créer dans le dossier « Form »
class UserType extends AbstractType { public function buildForm(FormBuilderInterface $builder, array $options) { $builder ->add('nom') ->add('prenom') ->add('email') ; } public function configureOptions(OptionsResolver $resolver) { $resolver->setDefaults([ 'data_class' => User::class, ]); } }
En bas du formulaire, il y a une fonction qui s’appelle « configureOptions ». Cette fonction, comme son nom l’indique permet de configurer les options du formulaire. C’est ici que l’on y indique à quelle entité est rattachée ce formulaire.
Dans notre cas, on voit qu’il est bien rattaché à l’entité « User »
'data_class' => User::class
Les champs mappés et non mappés
Symfony a ajouté tous les champs de l’entité (nom, prenom, email) au formulaire. Ce sont des champs qui sont mappés à l’entité.
Il est possible d’ajouter des champs supplémentaires qui ne font pas partis de l’entité. Ce sont des champs non mappés. Pour cela il faudra ajouter l’attribut ‘mapped’ =>false
public function buildForm(FormBuilderInterface $builder, array $options) { $builder ->add('nom') ->add('prenom') ->add('email') ->add('age', NumberType::class, [ 'mapped' => false ]) ; }
Ici, j’ai ajouté un champs « age », de type Number, qui n’est pas mappé.
Ajouter des options aux champs
Si tu regardes le champ que je viens de rajouter, tu constateras que j’ai ajouté « NumberType::class », ainsi qu’un tableau juste après.
La fonction « add », peux prendre jusqu’à 3 paramètres. Le premier est le nom du champ. Il doit être identique à l’attribut de l’entité. Le deuxième est le type de champs que l’on souhaite. Il en existe plusieurs, nous les verrons plus bas. Par défaut, Symfony attribue le bon type au champs en fonction des données contenu dans l’entité.
La propriété « prenom » de l’entité est de type string, Symfony créera donc un champ de type TexteType.
S’il y a une propriété de type « booleen »,Ssymfony créera une check box.
Le 3ème paramètre est un tableau d’option. On peut y passer différentes choses : le label, les attributs HTML du champ, des valeurs, etc
Par défaut, Symfony ne rajoute aucune option sur les champs.
Les différents types
Dans notre champs « age », j’ai indiqué à Symfony que ce champ sera un input de type « number ».
Je pourrais lui indiquer que je souhaite une checkbox, des boutons radio, une liste déroulante, etc. Chacun de ces type d’input correspond à un type de formulaire Symfony.
- TextType
- TextAreaType
- EntityType, utiliser pour les relations entre entité
- PasswordType, pour rentrer les mots de passe
- DateType, pour les format date
Tu pourras tous les retrouver en cliquant ici. Il en existe pour énormément de choses.
Utiliser le formulaire Symfony
Nous allons maintenant pouvoir utiliser le formulaire que nous avons créé.
Pour cela, il faut se rendre dans le contrôleur et créer une instance du formulaire.
Si tu n’as pas encore créé de contrôleur, il faut en créer un. Pour cela, tu peux le créer soit avec la commande Symfony :
symfony console make:controller DefaultController
Soit à la main en créant un dossier « Controller » dans « /src » puis en créant un fichier DefaultController.php à l’intérieur du dossier « Controller ».
namespace App\Controller; use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; use Symfony\Component\HttpFoundation\Response; use Symfony\Component\Routing\Annotation\Route; class DefaultController extends AbstractController { /** * @Route("/default", name="default") */ public function index(): Response { return $this->render('default/index.html.twig', [ 'controller_name' => 'DefaultController', ]); } }
Pour créer des instances de formulaire, il va falloir utiliser la fonction « createForm » depuis le contrôleur. On va lui passer en paramètre le nom de la classe de notre formulaire
public function index(): Response { $form = $this->createForm(UserType::class); return $this->render('default/index.html.twig', [ 'form' => $form->createView() ]); }
Ne pas oublier le « use App\Form\UserType; » à rajouter en haut de ton contrôleur.
On va ensuite envoyer notre formulaire à notre vue. N’oublie pas la fonction « createView() » qui permet de générer la vue du formulaire.



Pour afficher le formulaire Symfony dans ta vue
Du côté de la vue, c’est assez facile d’afficher notre formulaire :
Index.html.twig
{% block body %} {{ form(form) }} {% endblock %}
C’est la forme la plus simple pour afficher le formulaire.
Pour afficher chacun des champs indépendamment, on va utiliser les balises twig qui commencent par « form_ »
{% block body %} {{ form_start(form) }} {{ form_label(form.prenom) }} {{ form_widget(form.prenom) }} {{ form_label(form.nom) }} {{ form_widget(form.nom) }} {{ form_label(form.email) }} {{ form_widget(form.email) }} {{ form_rest(form) }} <button type="submit">Enregistrer</button> {{ form_end(form) }} {% endblock %}
Twig vas ensuite générer tout seul les balises HTML correspondantes.
Quelques explications sur les balises Twig « form »
Pour commencer un formulaire et créer la balise <form> il faut utiliser « form_start(form) »
Pour fermer la balise « </form>, il faut utiliser « form_end(form) »
Pour afficher le label d’un champs : « form_label(form.nomDuChamps) »
Pour afficher la balise input : « form_widget(form.nomDuChamps) »
Tu auras remarqué une balise « form_rest(form) » à la fin. Cette balise permet d’afficher les autres champs du formulaire qui n’ont pas été déclaré avec la balise « form_widget » précédemment.
Il est important de la mettre à la fin de chacun de tes formulaires. Cette balise affichera notamment le token du formulaire.
Si tu inspectes l’élément depuis ton navigateur, tu retrouveras ce token à la fin du formulaire. Il est de type « hidden ».
Pour plus d’informations sur les balises de formulaire twig, tu peux regarder ici
Le champ « submit »
Comme tu dois le savoir, chaque formulaire doit avoir un bouton de validation qui est de type « submit ».
Avec Symfony tu as 2 possibilités de le faire. Soit tu le rajoutes en tant que champs dans la classe de ton formulaire, soit tu le met uniquement dans la vue.
1ère possibilité : dans ta classe
public function buildForm(FormBuilderInterface $builder, array $options) { $builder ->add('nom') ->add('prenom') ->add('email') ->add('save', SubmitType::class) ; }
Ici, il n’y a pas besoin de lui indiquer que le champ « save » n’est pas mappé. Vu que c’est un du type « SubmitType », Symfony ne le mappe pas.
2ème possibilité : dans la vue
public function buildForm(FormBuilderInterface $builder, array $options) { $builder ->add('nom') ->add('prenom') ->add('email') ; }
index.html.twig
{% block body %} {{ form_start(form) }} {{ form_rest(form) }} <button type="submit">Enregistrer</button> {{ form_end(form) }} {% endblock %}
Il est recommandé d’utiliser plutôt la méthode « dans la vue ». Généralement la même classe de formulaire est utilisée pour l’ajout et la modification d’une entité. Le bouton d’enregistrement peut alors changer de nom (« Ajouter » / « Modifier »). Il est donc plus facile de gérer ça directement dans la vue.
Pour ce tutoriel, j’utilise la méthode « dans la vue ».
Cas spécifiques à l’utilisation du champ « submit »
Il peut arriver d’avoir plusieurs boutons de validation. Par exemple « Sauvegarder » et « Passer à l’étape suivante ». Dans ce cas, il est conseillé d’utiliser la méthode « dans la classe ».
Ainsi dans le contrôleur, tu pourras utiliser la méthode « isClicked() » pour savoir quel bouton a été cliqué et faire la bonne action en fonction.
if($form->get('save’)->isClicked()){ //Faire l'action qui correspond à ce bouton }
Récupérer les données
Maintenant que l’on a notre formulaire qui est fonctionnel, il va falloir que l’on récupère les données coté serveur.
Comme je le disais précédemment, un formulaire permet de modifier une entité. Il va donc falloir que l’on dise à notre formulaire quelle entité on va modifier.
Dans notre cas, nous allons créer une instance de la classe « User », la passer au formulaire puis la modifier à l’aide du formulaire
public function index(): Response { $user = new User(); $form = $this->createForm(UserType::class, $user); return $this->render('default/index.html.twig', [ 'form' => $form->createView() ]); }
Cependant, pour récupérer les données, il reste une étape à faire : indiquer à Symfony que le formulaire a été soumis et qu’il faut qu’il alimente l’instance « $user » avec les données du formulaire.
public function index(Request $request): Response { $user = new User(); $form = $this->createForm(UserType::class, $user); $form->handleRequest($request); if ($form->isSubmitted() && $form->isValid()){ dump($user);die; } return $this->render('default/index.html.twig', [ 'form' => $form->createView() ]); }
Il faut dans un premier temps, passer en paramètre de la fonction index la requête HTTP ($request).
Lorsque l’on va valider le formulaire, une requête de type POST vas être envoyée. Il faut donc que le formulaire capte cette requête et alimente l’instance « $user » avec. Cela se fait avec $form->handleRequest($request).
Par la suite, on vas vérifier que le formulaire a bien été soumis (« $form->isSubmitted()) et qu’il est valide ($form->isValid()). La validité se fait notamment à l’aide du token dont on a parlé précédemment. La vérification s’assure aussi que les données renseignées soient du bon type (texte, entier, booleen, …)
Grâce au « dump », on vas pouvoir s’assurer que notre instance « $user » a bien été alimenté par les données soumis dans le formulaire.



On voit bien que les données renseignées dans le formulaire se retrouve dans l’entité.
Enregistrer les données en base de données
Il faut maintenant que l’on enregistre notre nouvel utilisateur en base de données.
Cette partie est relativement simple. Il faut simplement persister notre instance « $user » puis l’enregistrer grâce à la fonction « flush » de l’entity manager.
public function index(Request $request, EntityManagerInterface $entityManager): Response { $user = new User(); $form = $this->createForm(UserType::class, $user); $form->handleRequest($request); if ($form->isSubmitted() && $form->isValid()){ $entityManager->persist($user); $entityManager->flush(); } return $this->render('default/index.html.twig', [ 'form' => $form->createView() ]); }
Pense à passer l’entity manager en paramètre de ton action « EntityManagerInterface $entityManager »
En amont, il faudra avoir configurer la connexion à ta base de données. Cette connexion se fait dans le fichier de configuration « .env.local ». Ce fichier est une copie de « .env ».
Le « .env » est le fichier de référence, qui peut être commiter sur ton repo. Il ne doit pas contenir les vraies informations de connexion. C’est simplement une sorte de référentiel pour savoir quelles variables d’environnement sont utilisées par notre application.
C’est dans le « .env.local » qu’il faudra renseigner les vraies informations et notamment celles de connexions à la base de donnés :
DATABASE_URL="mysql://root:[email protected]:5432/nomDeTaBDD?serverVersion=13&charset=utf8"
Si la configuration à ta base de données est correcte, tu devrais voir apparaitre dedans ton nouvel utilisateur.
Modifier un utilisateur existant
L’avantage avec les formulaires Symfony, c’est qu’ils peuvent être réutiliser pour la modification d’une entité.
Dans notre cas, on va modifier l’utilisateur enregistré.
Pour cela, on va modifier légèrement la route de l’action et en rajouter une.
/** * @Route("/user/add", name="user_add") * @Route("/user/{id}/edit", name="user_edit") */ public function index(?User $user, Request $request, EntityManagerInterface $entityManager): Response {
Ainsi nous avons 2 routes différentes pour la même action. Une route sera utilisée pour l’ajout et l’autre pour la modification.
Tu as vu que j’ai rajouté un paramètre dans l’action : « ?User $user ».
Vu que la 2ème route contient l’id de l’utilisateur que l’on souhaite modifier, Symfony vas automatiquement aller chercher l’instance « User » qui correspond à cet id. Si aucune instance n’est trouvée en base de données, une erreur 404 est retournée.
Plutôt pratique non ?
Dans le cas, où l’on est dans la première route, aucun utilisateur ne sera passé, en paramètre. L’utilisateur peut donc être « null ». C’est pour cette raison qu’il faut ajouter le « ? » avant.
Dans le cas où l’utilisateur est « null », on va créer une nouvelle instance « User ». Dans le cas où l’utilisateur est trouvé (modification), on va directement passer cette instance au formulaire. Ainsi, on va pouvoir modifier l’utilisateur déjà existant.
public function index(?User $user, Request $request, EntityManagerInterface $entityManager): Response { if(!$user){ $user = new User(); } $form = $this->createForm(UserType::class, $user); $form->handleRequest($request); if ($form->isSubmitted() && $form->isValid()){ if(!$user->getId()){ $entityManager->persist($user); } $entityManager->flush(); return $this->redirect($this->generateUrl('user_edit', ['id' => $user->getId()])); } return $this->render('default/index.html.twig', [ 'form' => $form->createView() ]); }
Dans le cas où l’utilisateur n’as pas encore d’ « id », c’est une nouvelle instance, il faut donc la persister. Dans le cas où il a déjà un « id », il n’est pas nécessaire de la persister.
A la suite de ça, on redirige vers la page de modification de l’utilisateur.
Formulaire Symfony : ce qu’il faut retenir
Si tu t’es lancé dans l’apprentissage du framework Symfony, tu as surement dû te confronter aux formulaires. Au début, il n’est pas évident de comprendre leurs fonctionnements. En effet, ils sont très différents des formulaires « classiques » en HTML. Ici, il faut créer une classe, la mapper avec une entité, créer une instance dans le contrôleur, etc …
Mais les formulaires Symfony sont extrêmement puissant et te permettrons de gagner beaucoup de temps par la suite. Une fois que tu as compris comment créer des formulaires, avec une seule commande, tu iras très vite. Les formulaires Symfony ont aussi l’avantage d’être réutilisable.
Du moment que tu en as créé un pour une entité particulière, tu peux le réutiliser n’importe où. C’est l’avantage de dissocier la partie logique du formulaire de la partie vue (HTML).
Lorsque tu créé une classe de formulaire, n’oublie pas de rajouter « Type » à la fin du nom de ta classe. Par exemple « UserType ». C’est une convention de Symfony
Chaque classe de formulaire que tu vas créer sera reliée à une de tes entités. Cette entité sera renseignée dans la fonction « configureOptions » grâce à la clé « data_class ».
Chaque champs de ton formulaire aura un type (TextType, DateTimeType, EntityType,…). Pour les champs mappés Symfony arrive à déterminer automatiquement quel type il faut. Pour les champs non mappés, il faudra que tu le spécifies.
Les formulaires Symfony, ont l’avantage d’être décorrélés de la vue. Dans les vues, tu devras utiliser les balises Twig commençant par « form » pour afficher l’ensemble de ton formulaire et de ses champs.
La récupération des données, se fait directement dans le contrôleur. Il faudra s’assurer que le formulaire a bien été soumis (isSubmitted) et est valide (isValid) pour pouvoir enregistrer les données en base grâce à l’ « entityManager ».
Maintenant que tu connais les bases pour créer des formulaires, la meilleure chose à faire et de s’entrainer à créer différents formulaires et tester des choses.