Skip to Content
SymfonyPartie 18 — Mini-jeux (exploration guidée)Terrain 6 — Doctrine : requêtes & perf

Terrain 6 — Doctrine : requêtes & perf

🔍 Renfort : 6.3, 6.6.

1. Méthode de repository ⭐⭐

🕹️ Mission : countPendingForUser(User $user): int avec le QueryBuilder.

✅ Solution

return (int) $this->createQueryBuilder('b') ->select('COUNT(b.id)') ->where('b.user = :u')->setParameter('u', $user) ->andWhere('b.status = :s')->setParameter('s', BookingStatus::Pending) ->getQuery()->getSingleScalarResult();

2. Corriger une injection SQL ⭐⭐

🕹️ Mission : sécurisez ->where("b.email = '$email'").

✅ Solution

->where('b.email = :email')->setParameter('email', $email)

3. Diagnostiquer un N+1 ⭐⭐⭐

🕹️ Mission : une liste affiche booking.slot.service.name par ligne → 101 requêtes. Que regardez-vous et que corrigez-vous ?

✅ Solution

Le profiler (onglet Doctrine, nombre de requêtes). Correction : fetch join

->addSelect('s', 'svc')->join('b.slot', 's')->join('s.service', 'svc')

4. Fetch join complet ⭐⭐⭐

🕹️ Mission : chargez les réservations avec leur slot et service en une requête.

✅ Solution

$this->createQueryBuilder('b') ->addSelect('s', 'svc')->join('b.slot', 's')->join('s.service', 'svc') ->getQuery()->getResult();

5. Piège du join sans addSelect ⭐⭐⭐

🕹️ Mission : pourquoi ->join('b.slot', 's') seul ne corrige pas toujours le N+1 ?

✅ Solution

join filtre mais n’hydrate pas la relation ; il faut addSelect('s') pour charger réellement les slots.

6. Projection DTO ⭐⭐⭐⭐

🕹️ Mission : listez id, email, status en objets légers (pas d’entités).

✅ Solution

$this->getEntityManager()->createQuery( 'SELECT NEW App\Dto\BookingListItem(b.id, b.customerEmail, b.status) FROM App\Entity\Booking b' )->getResult();

7. Pagination ⭐⭐⭐

🕹️ Mission : paginez avec offset/limit + total correct.

✅ Solution

$q = $this->createQueryBuilder('b')->setFirstResult(($p-1)*$per)->setMaxResults($per)->getQuery(); return new \Doctrine\ORM\Tools\Pagination\Paginator($q); // count() = total

8. Ajouter un index ⭐⭐

🕹️ Mission : indexez la colonne status fréquemment filtrée.

✅ Solution

#[ORM\Index(name: 'idx_booking_status', columns: ['status'])] class Booking {}

(Puis une migration.)

9. Traiter 500k lignes ⭐⭐⭐⭐

🕹️ Mission : parcourez 500 000 entités sans saturer la mémoire.

✅ Solution

foreach ($q->toIterable() as $i => $e) { /* traiter */ if ($i % 500 === 0) { $em->flush(); $em->clear(); } } $em->flush();

10. COUNT sécurisé ⭐

🕹️ Mission : comptez les réservations Pending.

✅ Solution

$repo->count(['status' => BookingStatus::Pending]);

Terrain suivant : Twig & composants.