• 12 heures
  • Difficile

Ce cours est visible gratuitement en ligne.

course.header.alt.is_certifying

J'ai tout compris !

Mis à jour le 16/05/2024

Sauvegardez des données en base

Découvrez l’EntityManager

Maintenant que nous savons comment récupérer des données envoyées dans un formulaire, il est temps de voir ce que nous allons en faire. Bien entendu, plein de cas de figure existent, mais dans notre cas, la réponse est simple : nous voulons enregistrer des éléments dans la base de données. Amélie sera certainement très contente d’avoir des formulaires pour ajouter des auteurs, des livres ou des éditeurs, mais s'ils n’ajoutent rien en base de données, ça ne sera pas très utile.

Pour l’instant, nous recevons à la sortie de nos formulaires une entité hydratée avec les données de la requête. Par exemple, un objet  Author  avec tout ce que l’utilisateur a saisi dans son formulaire. Ça tombe bien, nos entités ont toutes une représentation en base de données. Comme c’est Doctrine qui gère nos relations à cette base de données, il va falloir trouver un moyen de lui dire d’écrire le contenu de notre objet dans la base. C’est le travail d’un composant incontournable de Doctrine : l’EntityManager.

Cet EntityManager sera le point d’entrée unique de toutes vos opérations d’écriture en base de données, que ce soient les insertions, les mises à jour ou les suppressions.

Son fonctionnement est un peu particulier : il fonctionne selon un principe comparable aux transactions de base de données. Concrètement, cela veut dire que vous pourrez lui dire de préparer plein d’opérations différentes, rien ne sera effectué tant que vous n’aurez pas validé votre travail.

Voyons maintenant comment nous allons nous en servir.

Revenez sur les dépendances

Pour nous servir de l’EntityManager, il va falloir le rendre disponible là où nous en avons besoin. Prenons l’exemple de notre classe de controller  Admin\AuthorController  :

<?php
class AuthorController extends AbstractController
{
    // …
    #[Route('/new', name: 'app_admin_author_new', methods: ['GET', 'POST'])]
    public function new(Request $request): Response
    {
        $author = new Author();
        $form = $this->createForm(AuthorType::class, $author);
        
        $form->handleRequest($request);
        if ($form->isSubmitted() && $form->isValid()) {
            // on aimerait bien avoir l’EntityManager ici pour enregistrer notre auteur…
        }

Il existe une réponse simple à ce problème : l’injection de dépendance.

La quoi ? On ne va pas juste instancier notre EntityManager ?

Eh non ! L’injection de dépendance est justement l’inverse. Jusqu’à présent vous aviez certainement l’habitude de gérer vos dépendances comme ceci :

<?php
class MaSuperClasse
{
    private MaDependance $dependance;
    
    public function __construct()
    {
        $this->dependance = new MaDependance();
    }
}

Ceci pose problème, car si vous avez besoin de changer la classe  MaDependance  (changer son nom, ou utiliser une autre classe), vous devez réécrire tout le code qui l’utilise, dont celui-ci.

Dans Symfony, nous n’instancions plus les dépendances dont nous avons besoin, nous les demandons.

<?php
class MaSuperClasse
{
    private MaDependance $dependance;
    
    public function __construct(MaDependance $dependance)
    {
        $this->dependance = $dependance;
    }
}

C’est en fait beaucoup plus simple, car nos dépendances ont souvent elles-mêmes des dépendances, qui ont des dépendances, etc.

En plus, nous ne voudrions pas avoir deux connexions à la base de données en même temps, ce qui risque d’arriver si nous ne faisons pas attention en instanciant tout à la main. Symfony, lui, gère ce souci en nous fournissant toujours le même EntityManager.

Enfin, plutôt que d’instancier une classe concrète, nous allons demander un objet qui réponde à une interface. Comme ça, si la classe concrète change de nom, ça ne changera pas notre code, du moment que la nouvelle respecte l’interface.

<?php
class MaSuperClasse
{
    private DependanceInterface $dependance;
    
    public function __construct(DependanceInterface $dependance)
    {
        $this->dependance = $dependance;
    }
}

D’accord, ça a l’air super, mais comment on fait ?

Nous allons simplement demander notre dépendance à Symfony ! Dans un controller, vous pouvez demander n’importe quel objet disponible dans votre application simplement en typant les arguments de vos méthodes, vous l’avez d’ailleurs déjà fait avec l’objet  Request  :

<?php
// …
public function new(Request $request): Response
// …

Vous avez demandé la requête en cours, et vous l’avez reçue. Dans n’importe quelle méthode d’une classe de controller, ou même dans son constructeur, demandez un objet et Symfony vous le donne ! Et dans les autres classes de votre application, demandez une dépendance dans un constructeur, et Symfony vous la fournira. Si nous mettons à jour le controller pour recevoir l’EntityManager, en demandant son interface :

<?php
// …
use Doctrine\ORM\EntityManagerInterface;
// …
class AuthorController extends AbstractController
{
    // …
    public function new(Request $request, EntityManagerInterface $manager): Response
    // …

Et voilà. L’EntityManager est disponible dans votre méthode  new  . Utilisons-le tout de suite pour sauvegarder notre entité en base de données !

Sauvegardez les changements en base de données

Vous avez reçu des données de votre formulaire, et vous avez un EntityManager. Il ne vous reste plus qu’à combiner les deux. Nous allons lui demander d’enregistrer une nouvelle entité dans la base de données, ce qui créera une nouvelle ligne dans la table qui correspond au mapping de cette entité.

Pour l’instant, l’EntityManager ne sait pas qu’il doit enregistrer une nouvelle entité en base de données. Ce qu'il va se passer maintenant :

  1. Pour indiquer à l'EntityManager qu’il doit prendre en compte une nouvelle entité, il suffit de la passer à sa méthode  persist  . 

  2. Une fois ceci fait, vous pouvez indiquer à l’EntityManager que vous avez fini votre travail grâce à la méthode  flush  . 

  3. L’EntityManager va alors calculer la différence entre l’état de la base de données et les entités dont il a connaissance à cet instant précis. Comme il n’a connaissance que de l’entité Author que nous venons de “persister”, et qu’elle n’existe pas dans la base de données, il va insérer une nouvelle ligne correspondant à notre entité dans la table  author  .

<?php
// …
    public function new(Request $request, EntityManagerInterface $manager): Response
    {
        $author = new Author();
        $form = $this->createForm(AuthorType::class, $author);
        
        $form->handleRequest($request);
        if ($form->isSubmitted() && $form->isValid()) {
            $manager->persist($author);
            $manager->flush();
            // les données de $author sont maintenant dans la base de données
            // ici, $author->getId() vous renvoie l'id de la ligne qui vient d'être ajoutée en base de données
        }
    // …

Et voilà, vous savez récupérer des données grâce à un formulaire et les enregistrer dans la base de données !

Pour éviter cela, nous devons envoyer une redirection à l’utilisateur, quitte à le rediriger vers la même page. Cela forcera son navigateur à effectuer une nouvelle requête, vierge de toute donnée, ce qui videra le formulaire. Dans un controller, vous pouvez faire ça grâce à la méthode  $this->redirectToRoute()  à laquelle vous fournirez un nom de route :

<?php
// …
        if ($form->isSubmitted() && $form->isValid()) {
            $manager->persist($author);
            $manager->flush();
            
            return $this->redirectToRoute('app_admin_author_new');
        }
        
        return $this->render('admin/author/new.html.twig', [
            'form' => $form,
        ]);

Ici, si le formulaire a été soumis et qu’il est valide, on enregistre l’entité en base de données, puis on force l’utilisateur à faire une nouvelle requête grâce à une redirection. Sinon, on lui affiche simplement notre formulaire.

Faites le test : remplissez le formulaire, soumettez-le. Le formulaire est de nouveau vide. Mais si vous allez dans le Profiler, vous verrez en haut un bandeau jaune qui indique que vous venez d’une redirection :

Bandeau jaune indiquant la provenance d'une redirection
Bandeau jaune indiquant la provenance d'une redirection

Cliquez sur le nom de la route dans ce bandeau, et vous arrivez sur le profil de votre requête de soumission de formulaire… direction sur l’onglet Doctrine, qui vous détaille les requêtes qui ont été faites, y compris la requête d’insertion en base de données :

Détail des requêtes faites
Détail des requêtes faites

Vous venez d’enregistrer votre premier auteur en base de données, félicitations !

À vous de jouer

Contexte

Nos formulaires sont prêts et nous avons vu comment enregistrer en base de données les entités  Author  que le formulaire  AuthorType  nous permet de créer. Nous allons donc appliquer cette logique à tous nos formulaires.

Consignes

Il va vous falloir désormais mettre à jour les controllers correspondant aux formulaires des entités  Book  et  Editor  . Faites en sorte que les données renvoyées par les formulaires soient enregistrées en base de données correctement.

Vous pouvez une nouvelle fois vous inspirer du code réalisé pour l'entité  Author  .

En résumé

  • L’EntityManager de Doctrine est l’objet qui sert de point d’entrée pour toutes les écritures en base de données.

  • Il fonctionne par transaction : les entités doivent être passées à  persist  avant de pouvoir  flush  toutes nos opérations.

  • Pour l’utiliser dans un controller, demandez un argument typé  Doctrine\Orm\EntityManagerInterface  .

Il est temps de voir comment valider les données envoyées par l’utilisateur pour sécuriser notre application !

Exemple de certificat de réussite
Exemple de certificat de réussite