Modifier et supprimer des entités

Continuons sur cette lancée et complétons notre système CRUD pour supporter les opérations d’éditions de nos entités.

Mais avant cela, nous allons faire un léger aparté indispensable sur la notion d’entité gérée par Doctrine.

La notion d'entité gérée par Doctrine

Reprenons l’exemple de création d’un utilisateur :

<?php
# create-user.php

$entityManager = require_once join(DIRECTORY_SEPARATOR, [__DIR__, 'bootstrap.php']);

use Tuto\Entity\User;

// Instanciation de l'utilisateur

$admin = new User();
$admin->setFirstname("First");
$admin->setLastname("LAST");
$admin->setRole("admin");

// Gestion de la persistance
$entityManager->persist($admin);
$entityManager->flush();

// Vérification du résultat
echo "Identifiant de l'utilisateur crée : ", $admin->getId();

Bien que l’entité utilisateur soit bien configurée avec les annotations de Doctrine, lorsque nous instancions un utilisateur, il n’y a rien qui lie l’instance elle-même à Doctrine.

Il faut bien faire la distinction entre la classe User et les instances de celle-ci. Les annotations sur la classe permettent de dire à Doctrine comment gérer des instances de ce type.

Par contre, tant que nous ne demandons pas à Doctrine de le faire, nos instances n’auront aucun lien avec notre ORM.

Donc avant l’appel à la méthode persist de l'entity manager, l’instance n’était pas liée à Doctrine et n’etait donc pas gérée.

Par contre, dès que nous récupérons une entité via l'entity manager ou le repository ou bien dès que la méthode persist est appelée, le ou les entités sont gérées par Doctrine.

Ainsi, Doctrine surveille les modifications faites sur ces entités et appliquent ces changements à l’appel de la méthode flush.

Notion d'entité gérée par Doctrine
Notion d'entité gérée par Doctrine

Supprimer une entité

Pour bien voir, l’utilité de la notion d’entité gérée par Doctrine, nous allons essayer de supprimer l’utilisateur avec comme identifiant deux (2).

L'entity manager (oui encore lui) propose une méthode remove qui prend en paramètre l’entité que nous voulons supprimer.

<?php
# delete-user.php

$entityManager = require_once join(DIRECTORY_SEPARATOR, [__DIR__, 'bootstrap.php']);

use Tuto\Entity\User;

$index = 1;

$user = new User();
$user->setId($index + 1);
$user->setFirstname("First ".$index);
$user->setLastname("LAST ".$index);
$user->setRole("user");

$entityManager->remove($user);

On a beau mettre l’identifiant et toutes les informations de l’entité que nous voulons supprimer, en exécutant le code, nous obtenons une exception.

Fatal error: Uncaught Doctrine\ORM\ORMInvalidArgumentException:
 Detached entity User (id: 2, firstname: First 1, lastname: LAST 1, role: user)
 cannot be removed

L’entité est considérée comme détachée. Doctrine ne le gère pas et donc ne sait pas comment la supprimer. C’est une erreur assez courante et qui peut, sur de grand projet, facilement vous faire perdre beaucoup de temps.

Pour supprimer l’entité, nous allons donc commencer par le récupérer grâce au repository.

<?php
# delete-user.php

$entityManager = require_once join(DIRECTORY_SEPARATOR, [__DIR__, 'bootstrap.php']);

use Tuto\Entity\User;

$identifiant = 2;

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

// Récupération de l'utilisateur (donc automatiquement géré par Doctrine)
$user = $userRepo->find($identifiant);

$entityManager->remove($user);
$entityManager->flush($user);

// Récupération pour vérifier la suppression effective de l'utilisateur
$user = $userRepo->find($identifiant);

var_dump($user); // doit renvoyer NULL
Suppression de l'utilisateur
Suppression de l'utilisateur

Comme pour la création d’une entité, nous disons d’abord à Doctrine que nous souhaitons supprimer une entité, et ensuite nous validons l’opération en appelant la méthode flush.

Mise à jour d'une entité

Puisque appeler la méthode persist permet de gérer une entité. Il est tentant de créer une nouvelle entité avec les informations à jour (l’identifiant restant le même) puis de sauvegarder celle-ci avec persist.

Mais comme vous vous en doutez, cela ne fera pas une mise à jour. Et pour en avoir le cœur net, nous allons effectuer cette manipulation.

<?php
# update-user.php

$entityManager = require_once join(DIRECTORY_SEPARATOR, [__DIR__, 'bootstrap.php']);

use Tuto\Entity\User;

$index = 10;

$user = new User();
$user->setId($index + 1);
$user->setFirstname("First Modified ".$index);
$user->setLastname("LAST Modified ".$index);
$user->setRole("user");

$entityManager->persist($user);

$entityManager->flush();
Mise à jour de l'utilisateur en echec
Mise à jour de l'utilisateur en echec

En consultant la base de données, nous pouvons voir d’une nouvelle ligne a été crée dans la table.

Dès que nous appelons la méthode persist sur une entité pas encore gérée par Doctrine, à l’appel de la méthode flush, une nouvelle entrée sera forcément créer dans la base de données.

Donc comme pour la suppression d’une entité, nous allons d’abord commencer par la récupérer depuis le repository. Ensuite la mise à jour de cette entité se résume juste à modifier ses attributs puis à flusher.

<?php
# update-user.php

$entityManager = require_once join(DIRECTORY_SEPARATOR, [__DIR__, 'bootstrap.php']);

use Tuto\Entity\User;

$identifiant = 11;

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

// Récupération de l'utilisateur (donc automatiquement géré par Doctrine)
$user = $userRepo->find($identifiant);

$user->setFirstname("First Real Modification");
$user->setLastname("Last Real Modification");

$entityManager->flush();
Mise à jour de l'utilisateur avec comme identifiant : 11
Mise à jour de l'utilisateur avec comme identifiant : 11

Le comportement de Doctrine est très grandement influencé par l’état des entités. Il faut donc avant chaque mise à jour de celle-ci s’assurer que l’entité est bien gérée par Doctrine.

Fusionner une entité détachée

Nous avons la possibilité de gérer une entité qui n’est pas issue de la base de données sans passer la méthode persist. Prenons l’exemple classique où dans la session d’une application web, nous avons sérialisé un objet utilisateur.

Si nous dé-sérialisons ce même objet, il ne sera plus géré par Doctrine. Mais en utilisant la méthode merge de l'entity manager, l’entité est considérée comme si elle provenait de la base de données.

<?php
# update-user.php

$entityManager = require_once join(DIRECTORY_SEPARATOR, [__DIR__, 'bootstrap.php']);

use Tuto\Entity\User;

$user = unserialize("informations de l'utilisateur serialisées");

// Reprise en charge de l'objet par l'entity manager
$entityManager->merge($user);

$user->setFirstname("First Real Modification");

$entityManager->flush();

Le prénom de l’utilisateur sera mis à jour avec cette méthode.

La méthode merge ne doit être utilisée que dans un cadre où vous avez la certitude que les informations contenues dans l’objet sont déjà en parfaite synchronisation avec ceux dans la base de données. Sinon vous risquez d’écraser des modifications faites sur l’entité entre sa sérialisation et sa dé-sérialisation.


Nous pouvons maintenant effectuer les quatre (4) opérations de base avec Doctrine : créer, lire, modifier et supprimer une entité.

Pour l’instant, nous n’avons effleurer que la surface de Doctrine. Prenez le temps de tester la configuration et l’ensemble des options déjà présentées pour bien vous familiariser avec l’ORM phare de PHP.

Nous aurons l’occasion de bien approfondir tous ces concepts et d’en aborder de nouveaux dans les chapitres suivants.