← Retour aux articles
Performance et bases de données

Le problème N+1 dans Laravel : un arsenal complet de solutions au-delà de l'Eager Loading

Neuf méthodes pratiques pour résoudre le problème N+1 dans Laravel au-delà de l'Eager Loading de base : joins, hasManyThrough, agrégats, raw queries, vues de base de données, dénormalisation et plus encore.

Le problème N+1 dans Laravel : un arsenal complet de solutions au-delà de l'Eager Loading
Article en vedette ↗

Le problème N+1 dans Laravel : un arsenal complet de solutions au-delà de l'Eager Loading

Dans l'article précédent, nous avons vu comment l'Eager Loading résout le problème N+1. Mais que faire si vous avez des scénarios complexes ou besoin de performances maximales ? Explorons TOUTES les solutions possibles.

🎯 Méthode 1 : Eager Loading

La solution fondamentale qui couvre 80 % des cas :

// Eager Loading simple $posts = Post::with('author')->get(); // Chargement de plusieurs relations $posts = Post::with(['author', 'tags', 'comments'])->get(); // Chargement conditionnel $posts = Post::with(['author' => function($query) { $query->select('id', 'name')->where('active', true); }])->get();

🚀 Méthode 2 : Joins

Lorsque vous n'avez besoin que des données des tables liées :

// Obtenir les posts avec les noms d'auteurs en une seule requête $posts = Post::join('authors', 'posts.author_id', '=', 'authors.id') ->select('posts.*', 'authors.name as author_name') ->get(); // Utiliser directement dans le template : @foreach($posts as $post) <p>Author: {{ $post->author_name }}</p> @endforeach

⚡ Méthode 3 : Has Many Through

Pour les relations profondément imbriquées :

// Supposons qu'il existe une chaîne : Country -> User -> Post class Country extends Model { public function posts() { return $this->hasManyThrough(Post::class, User::class); } } // Maintenant, obtenir tous les posts d'un pays avec une seule requête $country = Country::with('posts')->find(1);

📊 Méthode 4 : Lazy Eager Loading

Lorsque vous ne savez pas à l'avance quelles relations seront nécessaires :

$posts = Post::all(); // Charger les auteurs pour la collection déjà récupérée $posts->load('author'); // Ou conditionnellement : if ($someCondition) { $posts->load('author', 'comments'); }

🎪 Méthode 5 : Agrégats

Lorsque vous n'avez besoin que de comptages ou de sommes :

// AU LIEU DE CECI (N+1 requêtes) : $posts = Post::all(); foreach($posts as $post) { echo $post->comments->count(); // Requête séparée pour chaque post } // FAITES CECI (2 requêtes) : $posts = Post::withCount('comments')->get(); foreach($posts as $post) { echo $post->comments_count; // La valeur est déjà préchargée }

🔥 Méthode 6 : Raw Queries

Pour des performances maximales dans des cas complexes :

$posts = DB::table('posts') ->leftJoin('authors', 'posts.author_id', '=', 'authors.id') ->leftJoin('comments', 'posts.id', '=', 'comments.post_id') ->select('posts.*', 'authors.name as author_name', DB::raw('COUNT(comments.id) as comments_count')) ->groupBy('posts.id') ->get();

💾 Méthode 7 : Vues de base de données

Pour les requêtes très complexes utilisées fréquemment :

CREATE VIEW post_details AS SELECT p.*, a.name as author_name, COUNT(c.id) as comments_count FROM posts p LEFT JOIN authors a ON p.author_id = a.id LEFT JOIN comments c ON p.id = c.post_id GROUP BY p.id, a.name;
// Utiliser dans Laravel comme une table normale $posts = DB::table('post_details')->get();

🏗 Méthode 8 : Dénormalisation

Parfois, il est plus simple d'ajouter des données redondantes :

// Ajouter un champ author_name à la table posts $post = new Post(); $post->title = 'Example'; $post->author_name = $author->name; // Enregistrer une copie $post->save(); // Maintenant, obtenir le nom de l'auteur sans JOIN : $posts = Post::all(); // Pas de N+1 !

📝 Méthode 9 : Scopes avec Eager Loading

Créez des scopes réutilisables :

class Post extends Model { public function scopeWithRelations($query) { return $query->with(['author', 'comments' => function($q) { $q->latest()->limit(5); }]); } } // Utilisez partout : $posts = Post::withRelations()->get();

🎯 Quand utiliser quoi ?

  • Eager Loading — pour la plupart des cas
  • Joins — lorsque vous n'avez besoin que des données des tables liées
  • Has Many Through — pour les relations profondes
  • Agrégats — pour les comptages et les sommes
  • Raw Queries — pour des performances maximales
  • Vues de base de données — pour les requêtes très complexes
  • Dénormalisation — pour les données fréquemment demandées

🔍 Comment trouver les zones problématiques ?

// Dans AppServiceProvider public function boot() { if (app()->environment('local')) { DB::listen(function ($query) { if (str($query->sql)->contains('where `id` in')) { Log::warning('Possible N+1 detected: ' . $query->sql); } }); } }

Conclusion : L'Eager Loading est une excellente solution, mais ce n'est pas la seule. Choisissez l'approche en fonction de la tâche spécifique et des exigences de performance.

Technologies et sujets

Tags de l'article

Aucun article ne correspond à ces filtres.

Vous avez un projet ou une idée à discuter ?

Parlons-en ↗