Image de couverture : Laravel : comment $with peut silencieusement multiplier vos requêtes SQL
tech

Laravel : comment $with peut silencieusement multiplier vos requêtes SQL

30 March 2026
6 min de lecture
6 vues
Sébastien Muler

Laravel : comment $with peut silencieusement multiplier vos requêtes SQL

Lors du développement d'applications Laravel, il est tentant de définir des relations à charger automatiquement directement dans les modèles Eloquent via la propriété $with. Cette approche semble pratique : plus besoin de penser à l'eager loading dans chaque requête. Pourtant, mal maîtrisée, elle peut devenir une source de dégradation silencieuse des performances. Cet article s'appuie sur une expérimentation publiée par George Bakoulis sur dev.to pour illustrer concrètement ce phénomène.


Qu'est-ce que $with en Eloquent ?

Dans un modèle Eloquent, la propriété $with permet de définir des relations qui seront toujours chargées en eager loading, quelle que soit la requête effectuée sur ce modèle.

class Post extends Model
{
    protected $with = ['author', 'category'];

    public function author(): BelongsTo
    {
        return $this->belongsTo(Author::class);
    }

    public function category(): BelongsTo
    {
        return $this->belongsTo(Category::class);
    }
}

À première vue, c'est pratique : on s'assure que author et category sont toujours disponibles sans risque de tomber dans le problème N+1. Mais le vrai danger apparaît lorsque plusieurs modèles définissent chacun leur propre $with et que ces modèles sont liés entre eux.


L'effet cascade : quand une requête en déclenche cinq

Prenons l'exemple concret de l'article source. Deux modèles ont chacun leur $with :

class Comment extends Model
{
    protected $with = ['post', 'author'];

    public function post(): BelongsTo
    {
        return $this->belongsTo(Post::class);
    }

    public function author(): BelongsTo
    {
        return $this->belongsTo(Author::class);
    }
}

class Post extends Model
{
    protected $with = ['author', 'category'];

    public function author(): BelongsTo
    {
        return $this->belongsTo(Author::class);
    }

    public function category(): BelongsTo
    {
        return $this->belongsTo(Category::class);
    }
}

Maintenant, exécutons une requête apparemment simple :

$comments = Comment::latest()->take(10)->get();

Sans $with : 1 seule requête

Si l'on commente la propriété $with dans Comment, cette requête ne génère qu'une seule requête SQL : la récupération des commentaires. Simple et efficace.

Avec $with sur Comment uniquement : 3 requêtes

En restaurant $with = ['post', 'author'] sur Comment, Laravel charge automatiquement les posts et les auteurs associés, ce qui donne :

  1. Récupération des 10 commentaires
  2. Chargement des posts liés
  3. Chargement des auteurs des commentaires

Soit 3 requêtes. C'est le comportement attendu et documenté de l'eager loading.

Avec $with sur Comment ET Post : 5 requêtes

Mais puisque Post définit lui-même $with = ['author', 'category'], le chargement des posts déclenche automatiquement le chargement de leurs propres relations :

  1. Récupération des 10 commentaires
  2. Chargement des posts liés aux commentaires
  3. Chargement des auteurs des commentaires
  4. Chargement des auteurs des posts (déclenché par le $with de Post)
  5. Chargement des catégories des posts (déclenché par le $with de Post)

Soit 5 requêtes pour une simple récupération de commentaires. Et cette cascade peut continuer si les modèles Author ou Category définissent eux aussi un $with.


Bonnes pratiques pour maîtriser vos chargements

1. Éviter $with dans les modèles sauf cas justifié

La règle générale chez MulerTech : ne définissez $with que si la relation est utilisée dans 100% des cas d'usage du modèle. Ce cas est rare. Préférez un eager loading explicite au moment de la requête :

// Chargement explicite et contrôlé
$comments = Comment::with(['post.category', 'author'])->latest()->take(10)->get();

Cette approche est plus verbieuse, mais elle vous donne une visibilité totale sur ce qui est chargé.

2. Utiliser without() pour exclure des relations ponctuellement

Si vous avez un modèle existant avec $with et que vous devez alléger une requête spécifique, Laravel fournit la méthode without() :

// On exclut les relations définies dans $with pour cette requête
$comments = Comment::without(['post', 'author'])->latest()->take(10)->get();

Utile pour des endpoints d'API légers ou des traitements batch qui n'ont pas besoin des relations.

3. Profiler vos requêtes avec Laravel Debugbar ou Telescope

L'outil indispensable pour détecter ce type de problème est Laravel Debugbar en développement, ou Laravel Telescope en staging. Ils vous montrent en temps réel le nombre de requêtes exécutées et leur contenu.

Une autre approche simple pour auditer rapidement :

DB::enableQueryLog();

$comments = Comment::latest()->take(10)->get();

dd(DB::getQueryLog());

Ce dump vous révèle immédiatement l'ensemble des requêtes déclenchées par votre appel.

4. Charger uniquement les colonnes nécessaires

Combinez l'eager loading explicite avec une sélection de colonnes pour réduire le volume de données transférées :

$comments = Comment::with([
    'post:id,title,category_id',
    'post.category:id,name',
    'author:id,name'
])->latest()->take(10)->get();

Cette syntaxe charge les relations tout en limitant les colonnes récupérées, ce qui peut faire une différence significative sur des tables volumineuses.


Conclusion

La propriété $with en Eloquent est un outil pratique, mais elle doit être utilisée avec discernement. Comme l'illustre parfaitement l'expérimentation de George Bakoulis, une cascade de $with entre modèles liés peut multiplier silencieusement le nombre de requêtes SQL sans que vous ne vous en rendiez compte.

⚠️ Une règle simple à retenir : plus un modèle est central dans votre domaine (comme Post ou User), plus il sera chargé dans des contextes variés, et moins il est approprié d'y définir un $with global.

En matière d'optimisation de performances sur des projets PHP/Symfony ou Laravel, la visibilité sur ce qui se passe au niveau de la base de données est primordiale. Prenez l'habitude de profiler vos requêtes régulièrement, de préférer l'eager loading explicite, et d'auditer les modèles qui définissent $with dans vos applications existantes.


Cet article s'inspire de l'expérimentation originale de George Bakoulis, publiée sur dev.to.

Partager cet article