<-

Retour

Symfony 6: ORM Doctrine

Symfony 6: ORM Doctrine

Ce support de cours mettra l'accent sur la gestion des bases de données avec Symfony.

Vous apprendrez à configurer et à utiliser Doctrine, l'ORM (Object-Relational Mapping) de Symfony, pour interagir avec votre base de données.

0

Partager sur twitterPartager sur FacebookPartager sur LinkdinPartager sur Telegram

Plan du cours

  1. Introduction à Doctrine
    1. Avantage de l'utilisation de Doctrine
    2. Installation et configuration de Doctrine
    3. Connexion à la base de données
    4. Créer la base de données
  2. Entity et Repository
    1. Définitaion des entité
    2. Définition du Repository
    3. Migrations: Mise à jour de la base de données
  3. Cas pratique: CRUD
    1. Création d'éléments
    2. Récuperer tous les films
    3. Récupérer un film avec son identifiant
    4. Mettre à jour une film
    5. Supprimer un film
  4. Rélations entre entité
    1. Création de la relation
    2. Ajouter des commenaires
    3. récuperer des commentaires

Prérequis


Objectifs

  • Comprendre le rôle de Doctrine dans la gestion de la base de données dans Symfony.
  • Configurer la connexion à la base de données dans Symfony.
  • Utiliser les attributes pour définir des entités et des relations.
  • Créer des requêtes de lecture, d'écriture et de suppression à l'aide du langage DQL (Doctrine Query Language).
  • Utiliser les migrations pour gérer les modifications de schéma de base de données.
  • Gérer les transactions et les relations entre les entités.

1. Introduction à Doctrine

Doctrine est une bibliothèque PHP puissante qui facilite la manipulation des données dans une base de données relationnelle.


Il s'agit d'un ORM (Object-Relational Mapping), ce qui signifie qu'il permet de représenter les données de la base de données sous forme d'objets PHP.


Ainsi, au lieu de manipuler directement des requêtes SQL, nous travaillons avec des entités et des objets PHP.


1.1. Avantages de l'utilisation de Doctrine

L'utilisation de Doctrine présente plusieurs avantages :

  • Productivité accrue : Avec Doctrine, vous pouvez vous concentrer sur les objets et la logique métier plutôt que sur les requêtes SQL. Cela permet d'accélérer le développement et de réduire les erreurs.
  • Portabilité : Doctrine prend en charge plusieurs SGBD (Systèmes de Gestion de Bases de Données) tels que MySQL, PostgreSQL, SQLite, etc. Vous pouvez facilement changer de SGBD sans avoir à réécrire votre code.
  • Gestion avancée des relations : Doctrine facilite la définition et la gestion des relations complexes entre les entités, telles que les relations One-to-One, One-to-Many et Many-to-Many.

1.2. Installation et configuration de Doctrine

Pour commencer à utiliser Doctrine, nous devons l'installer et le configurer dans notre projet Symfony. Voici les étapes à suivre :

Vous pouvez installer Doctrine en exécutant la commande suivante dans le répertoire racine de votre projet Symfony :

composer require orm

La configuration de Doctrine se fait dans le fichier config/packages/doctrine.yaml.


Voici un exemple de la configuration par défaut :


doctrine:
    dbal:
        url: '%env(resolve:DATABASE_URL)%'

        # IMPORTANT: Vous devez configurer la version du serveurs de votre base de données ici, ou dans
        # le fichier .env dans la variable DATABASE_URL
    orm:
        auto_generate_proxy_classes: true
        enable_lazy_ghost_objects: true
        report_fields_where_declared: true
        validate_xml_mapping: true
        naming_strategy: doctrine.orm.naming_strategy.underscore_number_aware
        auto_mapping: true
        mappings:
            App:
                is_bundle: false
                dir: '%kernel.project_dir%/src/Entity'
                prefix: 'App\Entity'
                alias: App

when@test:
    doctrine:
        dbal:
            # "TEST_TOKEN" is typically set by ParaTest
            dbname_suffix: '_test%env(default::TEST_TOKEN)%'

when@prod:
    doctrine:
        orm:
            auto_generate_proxy_classes: false
            proxy_dir: '%kernel.build_dir%/doctrine/orm/Proxies'
            query_cache_driver:
                type: pool
                pool: doctrine.system_cache_pool
            result_cache_driver:
                type: pool
                pool: doctrine.result_cache_pool

        framework:
            cache:
                pools:
                    doctrine.result_cache_pool:
                        adapter: cache.app
                    doctrine.system_cache_pool:
                        adapter: cache.system

1.3. Connexion à la base de données

Vous pouvez indiquer le dsn de connexion à la base de données dans le fichier .env à la racine du projet.


Dépendant de quelle base de données vous utilisez, la valeur peut changer.


Voici comment connecter à une base de données MySql:


# IMPORTANT: Vous devez configurer votre version de serveur ici ou dans onfig/packages/doctrine.yaml
DATABASE_URL="mysql://username:password@127.0.0.1:3306/basededonnees?serverVersion=8.0.32&charset=utf8mb4"

Vous pouvez retrouver l'équivalent pour d'autres base de données dans la documentation officielle de Symfony 6


1.4. Créer la base de données

Doctrine offre plusieurs commandes utiles pour gérer la base de données.


Par exemple, vous pouvez créer la base de données en utilisant la commande :


php bin/console doctrine:database:create

Ou plus facilement avec Symfony CLI :


symfony console doctrine:database:create


2. Entité et Repository

Les entités sont des classes PHP qui représentent les objets métier de notre application, ils nous permettent de construire des objets représentant une ligne d'une table dans la base de données.


Ils nous permette d'avoir une répresentation des tables sous forme d'objets de la classe Entity.


Les entités permettent aussi la synchronisation des colonnes des table avec les propriétés de la classe Entity ce qui facilite la création et la mise à jour des tables.


Les repositories, quant à eux, sont des classes qui fournissent des méthodes pour interagir avec les entités dans la base de données.


Ils ressemblent un peu à la couche Model dans l'architecture MVC.


Representation graphique du lien entre contrôleur, Entiry et Repository de Symfony 6. Les contrôlleurs peuvent créer des instance de l'entité, la peuplée avec les données de la requête, et l'enregistrer en utilisant le Repository. Ils utilise aussi les Repository pour récuperer les données dan la base de données sou forme d'entité.

Par conventions, les entités sont généralement situées dans le répertoire src/Entity et les repositories sont généralement situés dans le répertoire src/Repository.


2.1. Définition des entités

Pour créer une entité, nous allons utiliser une classe PHP et y définir les propriétés correspondantes aux colonnes de la table dans la base de données, et contiendra aussi des accesseurs et mutateurs.

<?php
// /src/Entity/Film.php

namespace App\Entity;

use App\Repository\FilmRepository;
use Doctrine\ORM\Mapping as ORM;

// Lier la classe Repository pour les films
#[ORM\Entity(repositoryClass: FilmRepository::class)]
class Film {
  #[ORM\Id]
  #[ORM\GeneratedValue]
  #[ORM\Column]
  private ?int $id = null;

  #[ORM\Column(length: 255)]
  private ?string $titre = null;

  public function getId(): ?int {
    return $this->id;
  }

  public function getTitre(): ?string {
    return $this->titre;
  }

  public function setTitre(string $titre): static {
    $this->titre = $titre;

    return $this;
  }
}

Dans cet exemple, nous avons défini une entité Film avec des attributs Doctrine pour définir chaque propriété représentant une colonne de la table :


  • une propriété id : générée automatiquement lors de la création.
  • une propriété titre : de type string et une taille maximale de 255 caractères.

Nous avons également utilisé des attributs de Doctrine pour spécifier quel FilmRepository est lié à l'entité.


2.2. Définition du Repository

Le repository associé à une entité nous permet d'effectuer des opérations de persistance et de récupération des données.


Un Repository doit étendre la classe ServiceEntityRepository pour hériter de certaines méthodes permettant de récupérer des données :


  • find($id, $lockMode = null, $lockVersion = null): Permet de récupérer un élément en utilisant son identifiant.
  • findOneBy(array $criteria, array $orderBy = null): Permet de récupérer une élément suivant des critère de sélection
  • findAll(): Récupérer tous les éléments
  • findBy(array $criteria, array $orderBy = null, $limit = null, $offset = null): Récupérer plusieurs éléments en suivant des critères de sélection.

<?php
// src/Repository/FilmRepository.php
namespace App\Repository;

use App\Entity\Film;
use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository;
use Doctrine\Persistence\ManagerRegistry;

/**
 * @extends ServiceEntityRepository<Film>
 * La classe hérite de ces méthodes
 * @method Film|null find($id, $lockMode = null, $lockVersion = null)
 * @method Film|null findOneBy(array $criteria, array $orderBy = null)
 * @method Film[]    findAll()
 * @method Film[]    findBy(array $criteria, array $orderBy = null, $limit = null, $offset = null)
 */
class FilmRepository extends ServiceEntityRepository
{
  public function __construct(ManagerRegistry $doctrine)
  {
    parent::__construct($doctrine, Film::class);
  }

  // Nous ajouterons plus tard ici des méthodes pour interagir avec la base de données
}

Nous injectons également un gestionnaire d'entités dans le constructeur de la classe, un objet de type ManagerRegistry.


Le gestionnaire d'entités est responsable de la persistance des objets en base de données et de la coordination des opérations entre l'application Symfony et la base de données.


2.3. Migrations: Mise à jour de la base de données

Maintenant que nous avons défini nos entités, nous devons mettre à jour la base de données pour refléter ces changements.


À chaque changement de vos entités, vous devrez migrer votre base de données.


Nous allons installer un package afin de créer ces migrations en ligne de commande :


composer require --dev symfony/maker-bundle

Ensuite, nous allons créer une migration en lançant la commande suivante :


php bin/console make:migration

Cette commande générera un fichier de migration basé sur les différences détectées entre vos entités et la structure actuelle de la base de données.


Ensuite, exécutez la commande suivante pour appliquer la migration :


php bin/console doctrine:migrations:migrate

Cela mettra à jour votre base de données avec les nouvelles tables et les relations définies dans vos entités.


3. Cas pratique: CRUD

3.1. Création d'éléments

Nous allons ajouter une méthode dans la classe FilmRepository nous permettant de supprimer un film de la base de données:


  1. utiliser la méthode persist() du gestionnaire d'entités pour ajouter lenregistrement de l'entité dans la prochaine transaction.

  2. Tester si nous voulons executer la transaction:

    1. Utiliser la méthode flush() pour exécuter les opérations sur la base de données (INSERT/UPDATE).
  3. Retourner l'instance du nouveaux film.


<?php
// src/Repository/FilmRepository.php

class FilmRepository extends ServiceEntityRepository {

  // ... Code précédent

  // 1. Méthode pour ajouter une Film dans la base de donnée
  public function save(Film $nouveauFilm, ?bool $flush = false) {

    // 1.1. Persiste l'entité Film dans le gestionnaire d'entités (Doctrine)
    $this->getEntityManager()->persist($nouveauFilm);

    // 1.2. Tester si nous devons executer la transaction
    if($flush){
      // 1.2.2. Effectue les opérations de base de données (INSERT/UPDATE)
      $this->getEntityManager()->flush();
    }

    // 1.3. Retourner l'instance du nouveau film 
    return $nouveauFilm;
  }
}

Nous pouvons maintenant l'utiliser dans un controlleur en injectant un objet de type FilmRepository.


Pour créer un élément dans la base de données, vous devez d'abord créer une instance de l'entité correspondante, puis définir les valeurs des propriétés nécessaires, en utilisant les méthodes d'accessibilité de l'entité.


Dans le Contrôleur FilmControlleur:


  1. Créer une route sur l'url /films acceptant les requêtes de type POST.
  2. Déclarer l'action associée, en injectant la requête et une instance du repository.
    1. Vérifier si le corps de la requête est vide ou ne contient pas le titre, retourner un message d'erreur.
    2. Sinon, Récuperer le titre du corps de la requête.
    3. Créer une instance de l'entité Film.
    4. Peupler l'entité avec les données reçues dans la requête.
    5. utiliser la méthode save() du repository pour enregistrer le film dans la base de données

<?php
// /src/Controller/FilmController.php

namespace App\Controller;

use App\Entity\Film;
use App\Repository\FilmRepository;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Annotation\Route;

class FilmController extends AbstractController {

  // 1. Route associé a l'url '/films' pour ajouter un film
  #[Route('/films', name: 'films.ajout', methods: ['POST'])]
  // 2. Action pour ajouter une film dans la base données en utilisant le repository
  function ajout(Request $req, FilmRepository $repository) {

    // 2.1 Vérifier si le corps de la requête est vide ou si la clé titre n'existe pas
    if (!$req->getContent() || !$req->getPayload()->has('titre')) {
      return $this->json(
        ['erreur' => 'Il manque le titre!'],
        Response::HTTP_BAD_REQUEST
      );
    }

    // 2.2. Récuperer les données du corps de la requête
    $titre = $req->getPayload()->get('titre');

    // 2.3. Créer une instance de l'entité et la peupler
    $film = (new Film())->setTitre($titre);

    // 2.5 utiliser le repository pour enregistrer le nouveau film dans la base de données
    $nouveauFilm = $repository->save($film, true);

    return $this->json(
      ['film' => $nouveauFilm->getTitre() . ' ajouté. ID: ' . $nouveauFilm->getId()],
    );
  }
}

3.2. Récupérer tous les films

Pour récupérer tous les films de la base de données, vous pouvez utiliser la méthode findAll() héritée de la classe ServiceEntityRepository.


Cette méthode renvoie un tableau d'objets entité Film.


Vous pouvez ensuite itérer sur ce tableau pour construire un tableau associatif des films à renvoyer en réponse JSON.


// Code précédent...

#[Route('/films', name: 'films.tout', methods: ['GET'])]
function tout(Request $req, FilmRepository $repository) {
  // Récupérer un tableau d'entités Film
  $films = $repository->findAll();

  // Les mettre dans un tableau associatif pour les envoyer en JSON
  $filmTableau = [];
  foreach ($films as $film) {
    $filmTableau[] = ['id' => $film->getId(), 'titre' => $film->getTitre()];
  }

  return $this->json(
    ['films' => $filmTableau]
  );
}

Dans cet exemple, nous avons ajouté une route /films avec une méthode GET pour récupérer tous les films de la base de données.


Nous utilisons la méthode findAll() du repository pour récupérer tous les objets entité Film, puis nous itérons sur ce tableau pour construire un tableau associatif des films avec les propriétés id et titre.


Enfin, nous renvoyons une réponse JSON contenant ce tableau.


3.3. Récupérer un film avec son identifiant

Pour récupérer un film à partir de son identifiant, vous pouvez utiliser la méthode find() héritée de la classe ServiceEntityRepository.


Cette méthode prend l'identifiant en paramètre et renvoie l'objet entité Film correspondant, ou null si aucun film n'est trouvé.


  1. Dans cet exemple, nous avons ajouté une route /films/{id} avec une méthode GET pour récupérer un film à partir de son identifiant.

  2. Nous utilisons la méthode find() du repository pour récupérer l'objet entité Film correspondant à l'identifiant fourni.

  3. Si le film n'est pas trouvé, nous renvoyons une réponse JSON avec un message d'erreur.

  4. Sinon, nous renvoyons une réponse JSON contenant les propriétés id et titre du film.


// Code précédent...

// 1. Route dynamique avec paramètre id pour récuperer un film
#[Route('/films/{id}', name: 'films.film', methods: ['GET'])]
function getById(FilmRepository $repository, int $id): Response {
    
  // 2. Récupérer une entité Film avec son identifiant
  $film = $repository->find($id);

  // 3. Vérifier si elle n'existe pas
  if (!$film) {
    return $this->json(
      ['erreur' => 'Film introuvable'], 
      Response::HTTP_NOT_FOUND);
  }

  // 4. Retourner le film en tableau associatifs
  return $this->json(
    ['film' => ['id' => $film->getId(), 'titre' => $film->getTitre()]]
  );
}

3.4. Récupérer un film avec son titre

Pour récupérer une entité à partir de critéres, vous pouvez utiliser la méthode findOneBy() héritée de la classe ServiceEntityRepository.


Cette méthode prend un tableau de critères de recherche en paramètre et renvoie le premier objet entité correspondant à ces critères, ou null si aucune entité n'est trouvé.


findOneBy(['propriété1' => 'critère1', 'propriété2' => 'critère2']) : Entity

Il existe l'équivalent pour recupérer plusieurs entités selon les critère:


findBy(['propriété1' => 'critère1', 'propriété2' => 'critère2']): Entity[]

Dans cet exemple, nous allons voir comment récuperer un film en utilisant son titre:


  1. Ajouté une route dynamique /films/titre/{titre} acceptant les méthode HTTP GET .
  2. Utilisé la méthode findOneBy() du repository en spécifiant le critère de recherche.
  3. Si le film n'est pas trouvé, nous renvoyons une réponse JSON avec un message d'erreur.
  4. Sinon, nous renvoyons une réponse JSON contenant les propriétés id et titre du film.

// Code précédent...

//1. Route dynamique pour récuperer un film avec son titre acceptant les methode HTTP GET
#[Route('/films/titre/{titre}', name: 'films.film.titre', methods: ['GET'])]
function getByTitre(Request $req, FilmRepository $repository, string $titre): Response {
  
  // 2. Récupérer une entité Film avec son titre
  $film = $repository->findOneBy(['titre' => $titre]);

  // 3. Vérifier si elle n'existe pas
  if (!$film) {
    return $this->json(
      ['erreur' => 'Film introuvable'], 
      Response::HTTP_NOT_FOUND
    );
  }

  // 4. renvoyer une réponse JSON contenant les id et titre du film.
  return $this->json(
    ['film' => ['id' => $film->getId(), 'titre' => $film->getTitre()]],
  );
}

3.5. Mettre à jour un film

Pour mettre à jour une entité, vous devez d'abord la récupérer, puis modifier les propriétés de l'entité en utlisant les setteurs avec les nouvelles valeurs.


Une fois modifier, vous pouvez réutiliser la méthode save() du repository pour enregistrer les modifications dans la base de données.


Dans cet exemple, nous allons ajouté la fonctionnalité permettant de mettre à jour un film:


  1. Créer une route /films/{id} avec une méthode PUT pour mettre à jour le titre d'un film.
  2. Si le corps de la requete est vide ou ne contient pas la clé titre, nous retournons une erreur.
  3. Sinon, nous utilisons la méthode find() du repository pour récupérer l'objet entité Film correspondant à l'identifiant fourni.
  4. Si le film n'est pas trouvé, nous renvoyons une réponse JSON avec un message d'erreur.
  5. Sinon, nous mettons à jour le titre de l'entité avec la nouvelle valeur et utilisons la méthode save() du repository pour sauvegarder les modifications.
  6. Enfin, nous renvoyons une réponse JSON avec les propriétés id et titre du film mis à jour.

// Code précédent...

// Route Dynamique pour mettre à jour le titre d'un film
#[Route('/films/{id}', name: 'films.update.titre', methods: ['PUT'])]
function updateTitre(Request $req, FilmRepository $repository, int $id): Response {
    
  // 2. Tester et Retourner une erreur si le  corps est vide ou pas de clé titre
  if (!$req->getContent() || !$req->getPayload()->has('titre')) {
    return $this->json(['erreur' => 'Titre obligatoire'], Response::HTTP_NOT_FOUND);
  }

  // 3. Récupérer une entité Film avec son identifiant
  $film = $repository->find($id);

  // 4. Retourner une erreur si le film n'existe pas
  if (!$film) {
    return $this->json(['erreur' => 'Film introuvable'], Response::HTTP_NOT_FOUND);
  }

  // 5. Mettre à jour le titre
  $film->setTitre($req->getPayload()->get('titre'));
  
  // 5. Effectuer une sauvegarde
  $filmAjour = $repository->save($film);

  // 6. Retourner le film mis à jour
  return $this->json(
    ['film' => [
      'id' => $filmAjour->getId(), 
      'titre' => $filmAjour->getTitre()
    ]],
  );
}

3.6. Supprimer un film

Pour supprimer une entité, vous devezd'abord la récupérer, puis utiliser la méthode supprimer() du repository pour supprimer l'entité de la base de données.

Pour ajouter la fonctionnalité de suppression de films, nous devons d'abord ajouter une méthode à la classe FilmRepository permettant de supprimer une entité Film reçue en paramètre:


  1. Ajouter une méthodes à la classe FilmRepository qui reçoit l'entité en paramètre.
    1. Supprimer l'entité lors la prochaine transaction
    2. Effectuer l' opération sur la base de donnée DELETE.

<?php
// src/Repository/FilmRepository.php

class FilmRepository extends ServiceEntityRepository {

  // ... Code précédent

  // 1. Méthode pour supprimer une Film dans la base de donnée
  public function remove(Film $film) : void {

    // 1.1. AJouter la suppression de l'entité Filmpour la prochaine transaction
    $this->getEntityManager()->remove($film);

    // 1.2. Effectue les opérations de base de données (DELETE) pour supprimer le film
    $this->getEntityManager()->flush();
  }
}

Puis, nous allons ajouté une route /films/{id} avec une méthode HTTP DELETE pour supprimer un film.


  1. Nous utilisons la méthode find() du repository pour récupérer l'objet entité Film correspondant à l'identifiant fourni
  2. Si le film n'est pas trouvé, nous renvoyons une réponse JSON avec un message d'erreur.
  3. Sinon, nous utilisons la méthode remove() du repository pour supprimer l'entité de la base de données.
  4. Enfin, nous renvoyons une réponse JSON avec un message de réussite.

// Code précédent...

#[Route('/films/{id}', name: 'films.supprimer', methods: ['DELETE'])]
function supprimer(Request $req, FilmRepository $repository, int $id): Response {
    
  // 1. Récupérer une entité Film avec son identifiant
  $film = $repository->find($id);

  // 2. Retourner une erreur si le film n'existe pas
  if (!$film) {
    return $this->json(['erreur' => 'Film introuvable'], Response::HTTP_NOT_FOUND);
  }

  // 3. Supprimer le film
  $statusSuppression = $repository->supprimer($film);

  // 4. Retourner un message de réussite
  return $this->json(
    ['message' => 'Le film ' . $film->getTitre() . ' est supprimé'],
    Response::HTTP_OK
  );
}


4. Relations entre entités

Dans le contexte de Doctrine, les relations entre entités sont définies à l'aide d'attributs au-dessus des propriétés correspondantes.


Il existe plusieurs types de relations :


  1. Relation ManyToOne : Une relation ManyToOne signifie que plusieurs entités de la classe source peuvent être liées à une seule entité de la classe cible. Par exemple, plusieurs commentaire peuvent être liés à un seule film.

  2. Relation OneToMany : Une relation OneToMany signifie que chaque entité de la classe source peut être liée à plusieurs entités de la classe cible. Par exemple, un film peut être lié à plusieurs commentaires.

  3. Relation ManyToMany : Une relation ManyToMany signifie que plusieurs entités de la classe source peuvent être liées à plusieurs entités de la classe cible. Par exemple, plusieurs films peuvent être liés à un seule acteur et plusieurs acteurs peuvent être liés à ** une seule film**


4.1. Création de la relation

Pour l'exemple nous allons ajouter une relation entre notre entités Film et un nouvelle entité Commentaire que nous allons créer.


Cette relation sera de type OneToMany, c'est à dire un film peut avoir plusieurs commentaires, et un commentaire n'est lié qu'a un seul film.


Commençons par définir notre entité Commentaire avec ses propriétés.


Pour garder les choses simple, ous supposerons que chaque commentaire possède un auteur, un message et une date.


L'entité Commentaire aura aussi une proriétés representant le film ou l'en indiquera le type (ManyToOne):


<?php
// /src/Entity/Commentaire.php

namespace App\Entity;

use App\Repository\CommentaireRepository;
use App\Entity\Film;
use Doctrine\ORM\Mapping as ORM;

#[ORM\Entity(repositoryClass: CommentaireRepository::class)]
class Commentaire {
  #[ORM\Id]
  #[ORM\GeneratedValue]
  #[ORM\Column]
  private ?int $id = null;

  #[ORM\Column(length: 255)]
  private ?string $auteur = null;

  #[ORM\Column(type: "text")]
  private ?string $message = null;

  #[ORM\Column(type: "date")]
  private ?string $date = null;

  #[ORM\ManyToOne(targetEntity: Film::class, inversedBy: 'commentaires')]
  private $film;

  public function getId(): ?int {
    return $this->id;
  }

  public function getAuteur(): ?string {
    return $this->auteur;
  }

  public function setAuteur(string $auteur): static {
    $this->auteur = $auteur;

    return $this;
  }
  public function getMessage(): ?string {
    return $this->message;
  }

  public function setMessage(string $message): static {
    $this->message = $message;

    return $this;
  }
  public function getDate(): ?string {
    return $this->date;
  }

  public function setDate(string $date): static {
    $this->date = $date;

    return $this;
  }

  public function getFilm(): ?Film {
    return $this->film;
  }

  public function setFilm(?Film $film): self {
    $this->film = $film;

    return $this;
  }
}

Maintenant, nous allons ajouter une relation OneToMany Dans l'entité Film.


Noter qu'il faut intialiser le tableau de commentaires dans le constructeur.


// src/Entity/Film.php

use App\Repository\FilmRepository;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\Common\Collections\Collection;
use Doctrine\ORM\Mapping as ORM;

/**
 * @ORM\Entity
 */
class Film
{
  // Code précédent...

  #[ORM\OneToMany(
    targetEntity:"App\Entity\Commentaire", 
    mappedBy:"film", 
    cascade:['persist']
  )]
  private $commentaires;

  public function __construct()
  {
      $this->commentaires = new ArrayCollection();
  }

  // Méthode pour récuperer la liste des commentaires
  public function getCommentaires(): Collection
  {
    return $this->commentaires;
  }
  
  // Méthode pour ajouter un commentaire
  public function addCommentaire(Commentaire $commentaire): Collection {
    // Permet d'enregistrer le commentaire en enregistrant le film
    $commentaire->setFilm($this);

    // Ajouter le commentaire à la liste
    $this->commentaires->add($commentaire);

    return $this->commentaires;
  }
}

Ici, nous avons utilisé l'attribut @ORM\OneToMany pour définir la relation OneToMany entre les entités Film et Commentaires.


Nous ajoutons aussi le paramètre cascade pour indiquer d'enregistrer les nouveaux commentaires lors de l'enregistement du film.


N'oubliez pas de migrer votre base de données pour ajouté la nouvelle table de commentaires.


4.2. Ajouter des commentaires

Maintenant nous pouvons ajouter à notre contrôleur une route pour pouvoir ajouter un commentaire un film:


  1. Si le corps de la requête est vide ou si les clés auteur et message ne sont pas réçues, retourner un message d'erreur.
  2. Récuperer l'entité film avec son id en utilisant le FilmRepository.
  3. Si le film n'exsite pas, retourner un message d'erreur.
  4. Recuperer les données du corps de la requête.
  5. Instancier et populer le commentaire avec les donnée réçues.
  6. Ajouter le commentaire dans le film.
  7. Enregistrer le film qui grâce à l'option cascade:['persist'] va aussi enregistrer le commentaire dans la base de données.

// /src/Controller/FilmController.php

// Code précédent ...

#[Route('/films/{id}/commentaires', name: 'film.commentaires.add', methods: ['POST'])]
public function ajouterCommentaire(
  Request $req,
  FilmRepository $repository,
  int $id
): Response {

  // 1. Il faut verifier si les clés auteur, message sont réçues dans le corps de la requete
  if (
    !$req->getContent() ||
    !$req->getPayload()->has('auteur') ||
    !$req->getPayload()->has('message')
  ) {
    return $this->json(
      ['erreur' => 'Veuillez fournir l\'auteur et le message!'],
      Response::HTTP_BAD_REQUEST
    );
  }

  // 2. Récuperer le film avec son id
  $film = $repository->find($id);

  // 3. Si le film n'exsite pas
  if (!$film) {
    return $this->json(
      ['erreur' => 'Film introuvable'],
      Response::HTTP_NOT_FOUND
    );
  }

  // 4. Recuperer les données du corps de la requête
  $auteur = $req->getPayload()->get('auteur');
  $message = $req->getPayload()->get('message');
  $date = date_create();

  // 5. Instancier et populer le commentaire avec les donnée réçues
  $commentaire = (new Commentaire())
    ->setAuteur($auteur)
    ->setMessage($message)
    ->setDate($date)
    ->setFilm($film);

  // 6. Ajouter le commentaire dans le film
  $film->addCommentaire($commentaire);

  // 7. Enregistrer le film et le commentaire grâce au cascade: persist
  $repository->save($film);

  return $this->json(['message' => 'Ajout du commentaire réussi!']);
}


4.3. Récuperer des commentaires

Pour récupérer les commentaire d'un film il suffit de récupérer le film puis d'utiliser la méthode getCommentaires de la class Film:


// /src/Controller/FilmController.php

// Code précédent ...

// Récuperer tous les commentaire d'un film
#[Route('/films/{id}/commentaires', name: 'film.get.commentaires', methods: ['GET'])]
public function getCommentaires(
  FilmRepository $repository,
  int $id
): Response {

  // Récuperer le film avec son id
  $film = $repository->find($id);

  // Si le film n'exsite pas
  if (!$film) {
    return $this->json(
      ['erreur' => 'Film introuvable'],
      Response::HTTP_NOT_FOUND
    );
  }

  // récuperer les commentaires automatiquement
  $commentaires = $film->getCommentaires();

  // Les transformer en un tableau associatif
  $commantairesJson = [];
  foreach ($commentaires as $c) {
    $commantairesJson[] = [
      'id' => $c->getId(),
      'auteur' => $c->getAuteur(),
      'message' => $c->getMessage()
    ];
  }

  return $this->json(['film' => $film->getTitre(), 'commentaires' => $commantairesJson]);
}


conclusion

Dans ce cours, nous avons exploré la gestion des bases de données avec Symfony 6 en utilisant Doctrine, l'ORM de Symfony.


Nous avons appris à configurer la connexion à la base de données, à définir des entités et des relations.


Puis nous avons vu comment créer des requêtes avec le langage DQL, à gérer les migrations et les transactions.


Doctrine facilite grandement la manipulation des données dans une base de données relationnelle en utilisant des objets PHP plutôt que des requêtes SQL.


Cela améliore la productivité, la portabilité et la gestion des relations complexes entre les entités.


Pour approfondir vos connaissances sur le sujet, voici quelques liens utiles :



Le prochain cours abordera les vues Twig avec Symfony.


Twig est un moteur de templates puissant et flexible intégré à Symfony qui facilite la création et la manipulation de vues dans les applications Symfony.


Nous verrons comment installer et configurer Twig, comment créer des templates Twig, comment utiliser les variables, les boucles et les conditions dans les templates.


Nous verrons aussi comment inclure des fragments de templates, ou faire hériter des templates d'autre templates pour composer votre front-end.


Ce cours vous permettra de maîtriser l'aspect front-end de vos applications Symfony en utilisant Twig pour générer des vues dynamiques et attrayantes.



Aller plus loin

Le prochain cours abordera les vues Twig avec Symfony et vous permettra de maîtriser l'aspect front-end de vos applications Symfony en utilisant Twig pour générer des vues dynamiques et attrayantes.


Commentaires

IsLoading