Aller au contenu principal

Doctrine

Présentation de la bibliothèque

Doctrine est un ORM (Object-Relational Mapper) pour PHP. Il permet de manipuler les données d'une base de données relationnelle comme s'il s'agissait d'objets PHP.

astuce

Doctrine vous évite d'écrire des requêtes SQL manuellement et rend votre code plus orienté objet.

info
  • Dans une architecture MVC (Modèle-Vue-Contrôleur), Doctrine serait utilisé pour la partie "Modèle".

  • Doctrine fait le lien entre vos objets PHP et les tables de votre base de données (MySQL, PostgreSQL, SQLite, etc.).

Pour installer la bibliothèque Doctrine ORM, nous ferons appel à Composer.

Voici la commande pour installer Doctrine avec Composer :

composer require doctrine/orm

Ensuite, vous devez configurer la connexion à la base de données et créer l'EntityManager.

L'EntityManager est l'objet principal de Doctrine, responsable de la gestion des entités (vos objets PHP).

use Doctrine\ORM\Tools\Setup;
use Doctrine\ORM\EntityManager;

$paths = ['/path/to/entities'];
$isDevMode = true;

// Configuration de la connexion à la base de données
$dbParams = [
'driver' => 'pdo_mysql',
'user' => 'root',
'password' => '',
'dbname' => 'my_database',
];

$config = Setup::createAttributeMetadataConfiguration($paths, $isDevMode);
$entityManager = EntityManager::create($dbParams, $config);
remarque

Dans cet exemple, nous utilisons des attributs (PHP 8+) pour définir le mapping (le lien entre l'objet et la table).

Une entité User.php pourrait ressembler à ceci :

#[Entity]
#[Table(name: 'tbl_users')]
class User
{
#[Id]
#[Column(type: 'integer')]
#[GeneratedValue]
private int $id;

#[Column(type: 'string')]
private string $name;

// Getters et Setters...
}

Il existe 3 concepts clés à maîtriser avec Doctrine :

  • Entité (Entity) : Une classe PHP qui représente une table dans la base de données. Les propriétés de la classe correspondent aux colonnes.
  • EntityManager : L'objet qui gère le cycle de vie des entités (chargement, sauvegarde, suppression).
  • Repository : Un objet qui permet de récupérer (lire) les entités depuis la base de données.

Le mapping (Attributs)

Le mapping permet de définir comment les propriétés de votre objet sont liées aux colonnes de la table.

remarque

Depuis PHP 8, il est recommandé d'utiliser les attributs natifs (ex: #[Column]) au lieu des annotations. Cela rend le code plus lisible et performant.

#[Entity]
class Product
{
#[Id]
#[GeneratedValue]
#[Column]
private int $id;

#[Column(length: 100)]
private string $name;

#[Column(type: 'integer')]
private int $price;
}

L'EntityManager (Écriture)

Pour interagir avec la base de données (insérer, mettre à jour, supprimer), on utilise l'EntityManager.

Les méthodes principales sont :

  • persist($entity) : Prépare l'entité à être sauvegardée (ne fait pas la requête SQL tout de suite).
  • flush() : Exécute toutes les requêtes SQL en attente.

Exemple pour créer un nouvel utilisateur :

$newUser = new User();
$newUser->setName('John Doe');

$entityManager->persist($newUser);
$entityManager->flush();

Consultez la documentation sur l'EntityManager pour en savoir plus : https://www.doctrine-project.org/projects/doctrine-orm/en/current/reference/working-with-objects.html

Les Repositories (Lecture)

Pour lire des données, on utilise un Repository. Chaque entité possède un repository associé.

On peut récupérer le repository via l'EntityManager :

$userRepository = $entityManager->getRepository(User::class);

Vous pouvez ensuite utiliser des méthodes prédéfinies :

// Trouver un utilisateur par son ID
$user = $userRepository->find(1);

// Trouver tous les utilisateurs
$users = $userRepository->findAll();

// Trouver par un critère spécifique
$user = $userRepository->findOneBy(['name' => 'John Doe']);

Consultez la documentation sur les Repositories pour en savoir plus : https://www.doctrine-project.org/projects/doctrine-orm/en/current/reference/working-with-objects.html#by-primary-key

--

Exemple pratique

Prenons l'exemple d'une gestion de produits.

Vous avez une entité Product avec un nom et un prix. Vous voulez insérer un nouveau produit en base de données, puis lister tous les produits existants.

// Création d'un nouveau produit
$product = new Product();
$product->setName('Ordinateur Portable');
$product->setPrice(899);

// Sauvegarde
$entityManager->persist($product);
$entityManager->flush();

// Lecture : Récupération de tous les produits
$productRepository = $entityManager->getRepository(Product::class);
$products = $productRepository->findAll();

// Affichage (sans Twig pour simplifier ici)
foreach ($products as $p) {
echo $p->getName() . ' : ' . $p->getPrice() . '€<br>';
}

Test de mémorisation/compréhension


Quel est le rôle principal de Doctrine ?


Dans une architecture MVC, où utiliseriez-vous Doctrine ?


Quelle méthode de l'EntityManager faut-il appeler pour exécuter réellement la requête SQL d'insertion ?


Quel objet utilise-t-on généralement pour effectuer des recherches (lecture) en base de données ?


Comment installez-vous Doctrine ORM ?



TP pour réfléchir et résoudre des problèmes

Dans ce TP, vous allez créer un système de gestion de tâches (Todo List) simple.

Vous aurez besoin d'une entité Task et d'un script pour les manipuler.

Une tâche possède un ID, un titre (string), un statut (boolean : terminée ou non) et une date de création.

Voici les étapes détaillées pour réaliser le TP :

  1. Création de l'entité Task :

Créez une classe Task dans un fichier src/Task.php.

remarque

N'oubliez pas de définir les attributs #[Entity], #[Table] et les colonnes appropriées. Pensez à générer les getters et setters nécessaires.

#[Entity]
#[Table(name: 'tbl_tasks')]
class Task
{
#[Id]
#[GeneratedValue]
#[Column(type: 'integer')]
private int $id;

#[Column(type: 'string')]
private string $title;

#[Column(type: 'boolean')]
private bool $isDone = false;

#[Column(type: 'datetime')]
private DateTime $createdAt;

public function __construct() {
$this->createdAt = new DateTime();
}
// ... Getters et Setters
}
  1. Configuration de la base de données :

Configurez l'EntityManager pour qu'il se connecte à une base de données SQLite (plus simple pour un TP local sans serveur MySQL).

// bootstrap.php
require_once "vendor/autoload.php";

use Doctrine\ORM\Tools\Setup;
use Doctrine\ORM\EntityManager;

$paths = [__DIR__ . "/src"];
$isDevMode = true;

$dbParams = [
'driver' => 'pdo_sqlite',
'path' => __DIR__ . '/db.sqlite',
];

$config = Setup::createAttributeMetadataConfiguration($paths, $isDevMode);
$entityManager = EntityManager::create($dbParams, $config);
  1. Création du schéma :

Avant d'utiliser l'entité, il faut créer la table correspondante dans la base de données. Pour ce TP, nous utiliserons l'outil en ligne de commande fourni par Doctrine.

# Si vous avez configuré le CLI de Doctrine
vendor/bin/doctrine orm:schema-tool:create

(Note : Si le CLI n'est pas configuré, vous pouvez utiliser la classe SchemaTool en PHP, voir la solution).

  1. Insertion de tâches :

Créez un script add_task.php qui crée une nouvelle tâche "Apprendre Doctrine" et l'enregistre en base.

$task = new Task();
$task->setTitle('Apprendre Doctrine');

$entityManager->persist($task);
$entityManager->flush();

echo "Tâche créée avec l'ID " . $task->getId();
  1. Liste des tâches :

Créez un script list_tasks.php qui récupère toutes les tâches et les affiche.

$taskRepository = $entityManager->getRepository(Task::class);
$tasks = $taskRepository->findAll();

foreach ($tasks as $task) {
echo $task->getTitle() . " - " . ($task->isDone() ? "Terminée" : "En cours") . "<br>";
}
  1. Mise à jour (Terminer une tâche) :

Modifiez le statut de la première tâche trouvée pour la marquer comme terminée.

$task = $taskRepository->find(1);
if ($task) {
$task->setIsDone(true);
$entityManager->flush();
echo "Tâche mise à jour.";
}

C'est tout ! Si vous suivez ces étapes, vous devriez être capable de créer, lire et mettre à jour des données avec Doctrine.

Une solution