PRÉSENTATION DU COURS

Créer des formulaires grâce aux classes de formulaires

Gestion des formulaire facilités grâce aux forms de symfony

Les forms dans Symfony

Si il y a bien quelquechose de très puissant dans Symfony c'est le composant de gestion de formulaire !

A lui seul, il gère les éléments suivants :

  • Rendu du formulaire dans la page
  • Gestion de l'envoi du formulaire
  • Validation des données
  • Normalisation des données
  • Protection CSRF

Mais par contre, avec un grand pouvoir vient de grandes responsabilités...

Le composant formulaire va vous faciliter la vie dans bien des cas, mais il peut s'avérer très complexe dans certains scénarios et litéralement vous pourrir la vie.

Il est donc important de bien comprendre ce composant et comment il marche pour éviter de se retrouver dans ces situations piège.

Vous n'êtes pas obligé d'utiliser le composant formulaire dans tous les cas, vous pouvez d'ailleurs utiliser Symfony sans jamais utiliser ce composant formulaire, mais ce serait dommage 😏

Installer le composant

Comme vous vous doutez, avec Symfony 4 les formulaires ne sont pas installés de base, nous allons donc devoir l'installer !

composer require form

Ok, maintenant que c'est installé, on va commencer à manipuler un peu !

Il n'y a qu'une seule règle dans les classes de formulaires : Étendre le type AbstractType

La plupart du temps, vos formulaires seront liés à vos entités car généralement ils vont serviront à les manipuler (Créer, mettre à jour)

Bon maintenant que l'on a gouté au MakerBundle, on va pas s'arrêter en si bon chemin !

Pour générer un nouveau formulaire, utilisez la commande : php bin/console make:form

Donnez lui un nom et une entité pour générer un formulaire de base d'une de vos entité.

Découvrir les classes "Type"

Au sein de cette classe, vous allez avoir deux méthodes dont une particulièrement intéressante : buildForm

C'est ici que nous allons renseigner tous les champs de notre formulaire, ainsi que les types, les options, etc... Enfin bref tout ce qui faut pour votre formulaire !

Pour l'instant on va laisser ça comme ça, on va d'abord voir comment utiliser le formulaire.

Afficher son formulaire

Avant de pouvoir créer quoi que ce soit, nous allons devoir agir dans nos controllers afin de créer le formulaire (je rappelle dans Symfony tout est un service 😉)

Par chance pour nous Symfony a un shortcut de la même manière que pour twig avec render, cette méthode est nommée createForm

⚠️Attention, Votre controller doit obligatoirement extends de AbstractController pour avoir accès à ces fonctions

Donc dans un controller au choix, nous allons ajouter une nouvelle méthode pour créer un formulaire :

// dans un controller, ceci est une méthode fictive d'exemple

/**
* @Route("/form", name="form_exemple")
*/
public function showForm() {

    $form = $this->createForm(ArticleFormType::class); // On fait passer la classe de formulaire au create form afin qu'il la génère

    return $this->render('ohdearaform.html.twig', ['form' => $form->createView()]); // on envoie ensuite le formulaire au template

}

Faites bien attention à faire passer la classe sous cette forme ArticleFormType::class ici les ::class donnent le nom de la classe directement, c'est un syntaxe que l'on va retrouver assez souvent dans Symfony

Maintenant que c'est fini avec le controller, passons au template

Pareil ici twig régale, on a des méthodes faites exprès pour afficher un formulaire !

Ce sont ces fonctions là dans la documentation (je vous laisserai aller voir par vous même pour aller plus loin) :

méthodes formulaire de symfony à voir dans twig

La manière la plus simple d'afficher un formulaire dans vos templates twig, c'est comme ça :

{# dans le prétendu template ohdearaform.html.twig #}
{% extends 'base.html.twig' %}

{% block title %}Homepage !{% endblock %}

{% block body %}
    <h1>Yay a form !</h1>
    {{ form_start(form) }}
        {{ form_widget(form) }}
        <button type="submit" class="btn btn-primary">Create!</button>
    {{ form_end(form) }}
{% endblock %}

Et Horray ! 🎊🎉 le formulaire s'affiche !

Comme vous pouvez le voir il fait de son mieux pour déterminer le type de chaque champ en fonction de l'entité ! On verra comment lui imposer le type plus tard

Ici nous utilisons trois balises :

  • form_start qui génère le tag form de départ
  • form_end qui génère le formulaire de fin
  • form_widget qui renvoie tous les champs du formulaire

Après, à nous de gérer le bouton d'envoi, il n'est pas mis automatiquement

Mais avec ces trois balises, nous pouvons gérer de cette manière beaucoup de formulaires (mais il existe des méthodes pour avoir plus de contrôle) !

Gérer le retour du formulaire

Bon, on a envoyé le formulaire, mais faut-il encore pouvoir le gérer derrière ! (Ou laissez comme ça pour le plaisir d'envoyer c'est vous qui voyez 😏)

Comme nous ne précisons pas d'url le formulaire sera renvoyé sur la même où le formulaire est affichée, par contre il renverra une requête POST

En général, la même méthode d'un controller va gérer le GET (affichage) et le POST (réception) du formulaire, il est donc très commun de voir ces deux logiques ensemble dans un controller

Alors, comment je récupère mon formulaire moi ?

Oui, pardon on s'écarte du sujet !

Si on reprends la méthode et que l'on la modifie un peu pour récupérer le formulaire quand il revient :

use Symfony\Component\HttpFoundation\Request; // important pour le request, prenez bien celui ci !
// dans un controller, ceci est une méthode fictive d'exemple

/**
* @Route("/form", name="form_exemple")
*/
public function showForm(Request $request) {

    $form = $this->createForm(ArticleFormType::class); 

    $form->handleRequest($request); // On récupère le formulaire envoyé dans la requête
    if ($form->isSubmitted() && $form->isValid()) { // on véfifie si le formulaire est envoyé et si il est valide
        dd($form->getData()); // Si c'est bon on dump les informations dans le formulaire (dd c'est essentiellement dump & die)
    }
    return $this->render('ohdearaform.html.twig', ['form' => $form->createView()]); // on envoie ensuite le formulaire au template

}

Pourquoi avoir la logique dans la même méthode ?

Par simplicité, on a pas besoin de redéfinir plusieurs fois le formulaire, et on gère directement si le formulaire n'est pas valide et non envoyé !

On a donc déjà pas mal de petites choses gérées sans utiliser de renvoi vers d'autres urls depuis nos controllers, donc c'est pas mal !

Maintenant pour l'enregistrer, on ajoute L'entityManager et on save :

Bien sur ici nous sommes dans des exemples fictifs, le mieux est de suivre avec les vrais formulaires de votre app PHP

// dans un controller, ceci est une méthode fictive d'exemple

/**
* @Route("/form", name="form_exemple")
*/
public function showForm(Request $request, EntityManagerInterface $em) {

    $form = $this->createForm(ArticleFormType::class); 

    $form->handleRequest($request); // On récupère le formulaire envoyé dans la requête
    if ($form->isSubmitted() && $form->isValid()) { // on véfifie si le formulaire est envoyé et si il est valide
        $article = $form->getData(); // On récupère l'article associé
        $em->persist($article); // on le persiste
        $em->flush(); // on save
    }
    return $this->render('ohdearaform.html.twig', ['form' => $form->createView()]); // on envoie ensuite le formulaire au template

}

Allez voir dans votre base et c'est enregistré !

wtf reaction

Pourquoi ça marche !?

Rassurez vous c'est tout à fait normal, comme nous l'avons dit, il n'y a pas de magie dans Symfony.

Vous vous rappellez quand vous avez défini le fomulaire via la commande make:form ? On vous a normalement demandé une entité avec laquelle lier le formulaire.

Hé bien simplement grâce à ça Symfony fait la liaison entre les deux, et au lieu de renvoyer de la donnée brute il nous renvoie la classe toute bien instanciée ! Ça c'est les choses qu'on apprécie !

Pour la partie magie, tout se passe dans la classe de définition du formulaire :

    public function configureOptions(OptionsResolver $resolver)
    {
        $resolver->setDefaults([
            'data_class' => Article::class,
        ]);
    }

C'est dans cette méthode que la liason ce fait, par exemple ici, on sait que notre formulaire est lié à Article car c'est la propriété data_class qui le défini.

Lorsque c'est le cas, Symfony va faire de son mieux pour mapper les champs du formulaire sur les champs de l'entité. (En tous cas dans la mesure du possible)

Tant que vous respectez la façon dont vous nommez les pripriétés de votre entité dans votre formulaire tout devrait bien se passer.

Si jamais ce formulaire n'est pas lié à une entité ?

Pas de soucis non plus on peut gérer ce cas !

Lorsqu'il n'y a pas d'entité associée $form->getData() renvoie un tableau associatif.

Imaginons que nous n'avons pas lié l'entité article à notre formulaire, nous aurions pu faire ce genre de chose :

// dans un controller, ceci est une méthode fictive d'exemple

/**
* @Route("/form", name="form_exemple")
*/
public function showForm(Request $request, EntityManagerInterface $em) {

    $form = $this->createForm(ArticleFormType::class); 

    $form->handleRequest($request); // On récupère le formulaire envoyé dans la requête
    if ($form->isSubmitted() && $form->isValid()) { // on véfifie si le formulaire est envoyé et si il est valide
        $article = $form->getData(); // On récupère l'article associé
        $article = new Article();
        $article->setTitle($data['title']);
        $article->setContent($data['content']);
        $article->setAuthor($data['author']);
    }

    return $this->render('ohdearaform.html.twig', ['form' => $form->createView()]); // on envoie ensuite le formulaire au template
}

De cette façon vous pouvez gérer l'enregistrement manuellement !

Que faire après avoir enregistré ?

Si vous avez déjà enregistré, vous allez voir que ça nous affiche le formulaire avec les informations que l'on vient d'entrer, pas très pratique...

Par contre, ce que l'on peut faire, c'est une redirection pour rediriger l'utilisateur vers une nouvelle page.

La redirection se fait de cette manière là : (Attention a bien avoir abstact controller)

// dans un controller, ceci est une méthode fictive d'exemple

/**
* @Route("/form", name="form_exemple")
*/
public function showForm(Request $request, EntityManagerInterface $em) {

    $form = $this->createForm(ArticleFormType::class); 

    $form->handleRequest($request); // On récupère le formulaire envoyé dans la requête
    if ($form->isSubmitted() && $form->isValid()) { // on véfifie si le formulaire est envoyé et si il est valide
        $article = $form->getData(); // On récupère l'article associé
        $em->persist($article); // on le persiste
        $em->flush(); // on save

        return $this->redirectToRoute('app_homepage'); // Hop redirigé et on sort du controller
    }
    return $this->render('ohdearaform.html.twig', ['form' => $form->createView()]); // on envoie ensuite le formulaire au template

}

Comme d'autres la fonction redirectToRoute est un petit helper de abstract controller, on pourrait le faire autrement mais c'est plus simple comme ça !

Conclusion

Ok, vous savez désormais utiliser les formulaire de manière basique

Désormais on va entrer un peu plus en détail sur comment faire vraiment ce que vous voulez avec !

Envie de discuter du contenu ?

logo twitter @GarnierKristen