Blog ENI : Toute la veille numérique !
Accès illimité 24h/24 à tous nos livres & vidéos ! 
Découvrez la Bibliothèque Numérique ENI. Cliquez ici
💥 Les 22 & 23 novembre : Accès 100% GRATUIT
à la Bibliothèque Numérique ENI. Je m'inscris !
  1. Livres et vidéos
  2. Symfony 6
  3. Accéder aux bases de données avec Doctrine
Extrait - Symfony 6 Développez des sites web PHP structurés et performants
Extraits du livre
Symfony 6 Développez des sites web PHP structurés et performants Revenir à la page d'achat du livre

Accéder aux bases de données avec Doctrine

Présentation et concepts

1. Les principes de l’ORM

Doctrine est une librairie permettant de gérer les interactions entre une application et une (ou plusieurs) base de données. Bien que ce ne soit pas la seule manière de communiquer avec une base de données (il existe d’autres librairies, comme Propel, l’extension PHP PDO, etc.), Symfony a clairement fait de Doctrine son option favorite. En effet, cette dernière est préconfigurée par défaut dans Symfony depuis la version 2 du framework.

L’ORM (Object-To-Relational Mapping), ou mapping objet relationnel, est un concept permettant d’associer des classes du modèle objet de PHP avec des tables de bases de données. Une fois cette association réalisée (le « mapping »), la manipulation des classes et des objets agit directement sur les données stockées en base, permettant ainsi au programmeur de simplifier les opérations basiques de gestion de données.

2. Architecture de Doctrine

Avant d’entrer dans le vif du sujet, il convient de définir certains concepts nécessaires à la compréhension du fonctionnement de Doctrine.

Doctrine se compose de plusieurs couches : DBAL, ORM et Entité.

images/07EI01.png

a. DBAL

La couche DBAL (Database Abstraction Layer) est la couche de plus bas niveau. Elle ne comporte aucune logique...

Installer et configurer Doctrine

1. Mise en place de Doctrine

Si vous avez créé votre application en utilisant le squelette d’application web à l’aide de l’une des commandes suivantes :

composer create-project symfony/skeleton:"6.4.*" mon_projet 

ou

symfony new mon_projet --version=6.4 --webapp 

alors Doctrine est déjà présent par défaut dans votre application comme en atteste le fichier composer.json situé à la racine du projet :

{ 
   "type": "project", 
   "license": "proprietary", 
   "require": { 
       "php": ">=8.1", 
       ... 
       "doctrine/doctrine-bundle": "^2.12", 
       ... 
   }, 
   ... 
} 

Dans le cas contraire, il est nécessaire d’installer le pack Symfony ORM via la commande :

composer require symfony/orm-pack 

2. Relation avec PDO

Comme nous l’avons vu précédemment, la couche DBAL de Doctrine est directement responsable de l’interaction avec la base de données. Pour ce faire, Doctrine utilise la librairie PDO (PHP Data...

Définition des entités et de leur mapping

La première étape consiste à créer des entités. Comme nous l’avons vu précédemment, une entité est une classe « spéciale », dans le sens où Doctrine est capable de l’associer à une table en base de données. Ce concept pouvant sembler flou au premier abord, illustrons-le au travers d’un exemple.

1. Règles de conception des entités

Imaginons que vous souhaitiez répertorier des livres dans une base de données, chaque livre ayant un id, un titre et une date de parution.

Ici, l’entité est une simple classe, contenant les différentes informations dans des propriétés :

<?php 
 
namespace App\Entity; 
 
class Livre 
{ 
    private $id; 
 
    private $titre; 
 
    private $dateParution; 
 
 
    /** 
     * @return integer  
     */ 
    public function getId() 
    { 
        return $this->id; 
    } 
 
    /** 
     * @param string $titre 
     * @return Livre 
     */ 
    public function setTitre($titre) 
    { 
        $this->titre = $titre; 
    
        return $this; 
    } 
 
    /** 
     * @return string  
     */ 
    public function getTitre() 
    { 
        return $this->titre; 
    } 
 
    /** 
     * @param \DateTime $dateParution 
     * @return Livre 
     */ 
    public function...

Manipulation des entités avec l’EntityManager

1. Le rôle de l’EntityManager

L’EntityManager est l’objet permettant d’effectuer les opérations liées à l’altération des données, à savoir les requêtes de type INSERT, UPDATE et DELETE.

Au niveau de votre application, même si vous manipulez des entités, qui correspondent aux données présentes en base, il ne suffit pas de modifier la valeur d’une propriété (au travers d’un mutateur) pour que Doctrine fasse la synchronisation en base de données. Vous devez gérer ces opérations avec l’EntityManager.

2. Insertion de données

Pour insérer une ligne dans une table donnée, il faut tout d’abord instancier l’entité correspondant à cette table :

use App\Entity\Livre; 
 
$livre = new Livre(); 

Ensuite, les données à insérer doivent être injectées au travers des mutateurs :

use App\Entity\Livre; 
 
$livre = new Livre(); 
$livre->setTitre('La ferme des animaux'); 
$livre->setDateParution(new \DateTime('1945-08-17')); 

Enfin, il ne reste plus qu’à demander à Doctrine d’envoyer la requête d’insertion vers la base de données :

use App\Entity\Livre; 
 
$livre = new Livre(); 
$livre->setTitre('La ferme des animaux'); 
$livre->setDateParution(new \DateTime('1945-08-17')); 
 
$em = $container->get('doctrine')->getManager(); 
$em->persist($livre); 
$em->flush(); 

Revenons sur cette dernière étape plus en détail. Dans un premier temps, nous récupérons l’EntityManager de Doctrine. Ce dernier peut être récupéré en invoquant la méthode getManager() du service doctrine.

Dans la majorité des situations, le code de récupération de l’EntityManager se trouve au sein d’un service de manipulation de données créé dans votre application. L’avantage de cette approche est que l’EntityManager Doctrine pourra y être injecté. Voici un exemple d’un tel service :

namespace App\Model; 
 
use App\Entity\Livre; 
use Doctrine\ORM\EntityManagerInterface; ...

Récupérer des entités

1. Le repository

a. Un rôle de centralisateur

Le repository est l’endroit où sont regroupées les requêtes relatives à une entité. La majorité est liée à la récupération de données (SELECT), mais le repository peut aussi tout à fait contenir des opérations de création, modification ou suppression. 

Chaque entité possède son propre repository. Il peut être matérialisé par une classe définie par le développeur, si la propriété repositoryClass de l’attribut #[ORM\Entity] est renseignée, ou, le cas échéant, par une classe interne à Doctrine. 

Nous avons utilisé un repository lors d’un exemple précédent (modification d’une entité). Nous l’utilisions pour récupérer une entité en fonction de la valeur de sa clé primaire :

$repository = $this->em->getRepository(App:Livre');  
  
$livre = $repository->find(1); 

Décomposons le code ci-dessus en plusieurs parties distinctes.

La récupération du repository

Pour récupérer le repository d’une entité donnée, vous devez invoquer la méthode getRepository() de l’EntityManager. En paramètre, celle-ci prend le FQCN (Fully Qualified Class Name) de l’entité dont le repository est souhaité. 

Comme vous avez pu le constater, App:Livre n’est pas un nom de classe, c’est un raccourci spécifique à Symfony qui correspond à App\Entity\Livre. Les deux sont tout à fait valides et peuvent être utilisés, le premier ayant l’avantage d’être plus court.

La récupération d’une entité

Dans un second temps, nous récupérons l’entité dont la clé primaire vaut 1, en invoquant la méthode find() sur le repository.

b. Les méthodes de base du repository

La méthode find() que nous venons d’utiliser fait partie des quelques méthodes définies par défaut dans un repository. Ces méthodes de base permettent la récupération d’une ou plusieurs entités en fonction de la valeur...

Fonctionnalités avancées

1. Les extensions Doctrine

Les extensions de Doctrine sont une fonctionnalité incontournable. Très pratiques, elles permettent d’ajouter certains comportements à vos entités, comme par exemple :

  • un slug : une propriété générée automatiquement et qui contient un identifiant lisible pour votre entité, parfaitement adapté aux URL (nous illustrerons cette extension par un exemple),

  • les traductions : le contenu de vos entités peut être traduit en plusieurs langues, 

  • l’ajout de propriétés created et updated : des dates mises à jour automatiquement lors de la création et de la dernière modification de l’entité.

Cette liste est non exhaustive, il existe bien d’autres extensions.

a. Installation

Ces extensions ne sont pas installées par défaut. Le bundle DoctrineExtensionsBundle les met à votre disposition.

Pour l’installer, il suffit d’utiliser la recette Symfony associée et donc d’exécuter la commande :

composer require stof/doctrine-extensions-bundle 

b. Utilisation d’un slug sur une entité

Le bundle est prêt à être utilisé. Configurons l’extension sluggable (pour la génération de slugs) sur notre entité Livre.

Le slug est une propriété...