L'eager loading en profondeur

Grâce à Eloquent dans Laravel, il devient particulièrement simple d’éviter le problème classique des requêtes N+1, car le framework propose des mécanismes de préchargement performants qui permettent de charger efficacement les relations entre modèles.

Larafrique
26/08/2025
L'eager loading en profondeur

Problème N+1

Le problème N+1 survient lorsqu’une requête principale est exécutée pour récupérer une collection d’objets (1 requête), mais qu’ensuite, pour chaque objet de cette collection, une nouvelle requête supplémentaire est lancée pour récupérer ses relations (N requêtes). Au total, tu obtiens N+1 requêtes, ce qui est très inefficace. Voici un exemple

1namespace App\Models;
2 
3use Illuminate\Database\Eloquent\Model;
4use Illuminate\Database\Eloquent\Relations\BelongsTo;
5 
6class Book extends Model
7{
8 /**
9 * Récupère l'auteur ayant écrit le livre.
10 *
11 * @return BelongsTo
12 */
13 public function author(): BelongsTo
14 {
15 return $this->belongsTo(Author::class);
16 }
17}

Maintenant récupérons tous les livres ainsi que leurs auteurs

1use App\Models\Book;
2 
3$books = Book::all();
4 
5foreach ($books as $book) {
6 echo $book->author->name;
7}

Sans optimisation, le code fait 1 requête pour récupérer la liste des livres, puis 1 requête supplémentaire pour chaque livre afin de trouver son auteur. Donc, avec 25 livres, on obtient 26 requêtes au total. Résultat des requêtes :

1-- On séléctionne les livres
2SELECT * FROM books;
3 
4-- Pour chaque passage dans la bloucle, on séléctionne chaque auteur du livre
5SELECT * FROM authors WHERE authors.id = 1; -- pour le premier livre
6SELECT * FROM authors WHERE authors.id = 2; -- pour le deuxième livre
7SELECT * FROM authors WHERE authors.id = 3; -- pour le troisième livre
8-- jusqu'à totaliser 25 requêtes

Qu’est-ce que l’Eager Loading ?

L’eager loading est une technique qui permet de charger les relations d’un modèle en avance, en même temps que la requête principale. Cela évite le problème classique N+1, où chaque élément de la collection déclenche une nouvelle requête pour charger ses relations.

Comment ça marche ?

Pour notre exemple, on peut utilises le préchargement (eager loading) pour réduire l'opération à seulement deux requêtes. Lors de la construction d’une requête, vous pouvez préciser quelles relations doivent être chargées de façon anticipée en utilisant la méthode with. Notre code deviens alors :

1$books = Book::with('author')->get();
2 
3foreach ($books as $book) {
4 echo $book->author->name;
5}

Pour cette opération, seulement deux requêtes seront exécutées : une pour récupérer tous les livres, et une pour récupérer tous les auteurs de ces livres. Résultat des requêtes :

1-- On séléctionne les livres
2SELECT * FROM books;
3 
4-- On séléctionne tous les auteurs des livres
5-- séléctionnés en une seule requête et Laravel
6-- se chargera de les associés aux livres
7SELECT * FROM authors WHERE authors.id IN (1, 2, 3,..., 25);

Précharger plusieurs rélations

On peut précharger plusieurs rélations en même temps. Dans ce cas, au lieu de passer le nom de la rélation en paramètre dans la méthode with, on passe plutôt un tableau

1$books = Book::with(['author', 'publisher'])->get();

Préchargement imbriqué

Pour précharger les relations d’une relation, vous pouvez utiliser la syntaxe par “points” (dot syntax) ou un tableu imbriqué. Par exemple, chargons de façon anticipée tous les auteurs d’un livre ainsi que tous les contacts personnels de chaque auteur

1// Synthax “points” (dot syntax)
2$books = Book::with('author.contacts')->get();
3 
4// Tableau imbriqué
5$books = Book::with([
6 'author' => [
7 'contacts',
8 'publisher',
9 ],
10])->get();

Chargement dynamique (Lazy Eager Loading)

Parfois, il peut être nécessaire de charger de manière anticipée une relation après que le modèle parent ait déjà été récupéré. Par exemple, cela peut être utile si vous devez décider dynamiquement de charger ou non les modèles associés :

1use App\Models\Book;
2 
3$books = Book::all();
4 
5if ($condition) {
6 $books->load('author', 'publisher');
7}

Astuces et conseils

Préchargement automatique

⚠️ Cette fonctionnalité est actuellement en version bêta afin de recueillir les retours de la communauté. Son comportement et ses fonctionnalités peuvent évoluer, même lors de mises à jour mineures.

Dans de nombreux cas, Laravel peut précharger automatiquement les relations auxquelles vous accédez. Pour activer ce chargement automatique, vous devez appeler la méthode Model::automaticallyEagerLoadRelationships dans la méthode boot du AppServiceProvider de votre application

1use Illuminate\Database\Eloquent\Model;
2 
3/**
4 * Bootstrap any application services.
5 */
6public function boot(): void
7{
8 Model::automaticallyEagerLoadRelationships();
9}

Si vous ne souhaitez pas activer le préchargement automatique de manière globale, vous pouvez tout de même activer cette fonctionnalité pour une seule instance de collection Eloquent en appelant la méthode withRelationshipAutoloading sur cette collection

1$books = Book::where('published', true)->get();
2 
3$books->withRelationshipAutoloading();

Prévention du Lazy Loading

Comme nous l’avons vu précédemment, le chargement anticipé (eager loading) des relations peut souvent améliorer considérablement les performances de votre application. Par conséquent, si vous le souhaitez, vous pouvez demander à Laravel d’empêcher systématiquement le chargement paresseux (lazy loading) des relations. Pour ce faire, vous pouvez appeler la méthode preventLazyLoading fournie par la classe de base des modèles Eloquent. En général, cette méthode est appelée dans la méthode boot de la classe AppServiceProvider de votre application.

1use Illuminate\Database\Eloquent\Model;
2 
3/**
4 * Bootstrap any application services.
5 */
6public function boot(): void
7{
8 Model::preventLazyLoading(! $this->app->isProduction());
9}

Optimisation

Quand vous préchargez les relation, Laravel va sélectionner tous les champs de la table démandée, ce qui n'est pas très optimisé quand on veut seulement utilisé quelques champs, par exemple :

1@php
2 $books = Book::with('author')->get();
3@endphp
4 
5@foreach($books as $book)
6 <div>
7 <h4>{{ $book->title }}</h4>
8 <div>
9 <p>{{ $book->published_at->diffForHumans() }}<p>
10 <p>{{ $book->author->full_name }}</p>
11 </div>
12 </div>
13@endforeach

Dans cet exemple, on a préchargé les auteurs des livres pour afficher seulement leurs noms dans la boucle. Mais dans la requête on a séléctionné tous les champs dans la table. Ce qui n'est pas très optimisé. La solution est de préciser les champs qu'on veut via la méthode with.

1@php
2 $books = Book::with('author:id,full_name')->get();
3@endphp
4 
5@foreach($books as $book)
6 <div>
7 <h4>{{ $book->title }}</h4>
8 <div>
9 <p>{{ $book->published_at->diffForHumans() }}<p>
10 <p>{{ $book->author->full_name }}</p>
11 </div>
12 </div>
13@endforeach

On doit préciser l'id pour que Laravel l'utilise pour associer le model à la rélation

En savoir plus

Pour approfondir le sujet et consulter la documentation officielle, voici quelques ressources utiles :

Sommaire