Formation PUB030 : Laravel, 2019 Les relations avec Eloquent ORM

22.7 Problème de « N+1 requêtes »


Si vous travaillez en chargement différé, ou « lazy loading », les performances pourraient être affectées par le problème de « N+1 requêtes ».

Par exemple, si vous avez 10 catégories et que vous souhaitez exécuter le travail suivant :

Contrôleur Laravel (PHP)

$categories = Categorie::all();

 

foreach ($categories as $categorie) {

    echo $categorie->produits->first()->prix;

}

Eloquent effectuera une requête à la base de données pour retrouver la liste des catégories puis il exécutera 10 fois la requête permettant de retrouver le prix d'un produit.

with()

Pour régler ce problème, on pourra forcer le chargement hâtif, ou « eager loading » à l'aide de la méthode with(). 

La méthode with() prend en paramètre le nom de la méthode définissant la relation (ici : produits()), sans les parenthèses.

Contrôleur Laravel (PHP)

$categories = Categorie::with('produits')->get();

 

foreach ($categories as $categorie) {

    echo $categorie->produits->first()->prix;

}

Remarquez que with() reçoit en paramètre le nom d'une propriété dynamique et non le nom d'une table. Donc, pour que ceci fonctionne, le modèle Categorie doit définir une relation portant le nom produits().

Une instruction avec with() réalise deux requêtes SQL. Dans l'exemple précédent :

  • une première requête va chercher la liste des catégories;
  • une seconde requête va chercher la liste des produits de chacune des catégories.

La syntaxe à utiliser avec findOrFail() est la suivante :

Contrôleur Laravel (PHP)

$categorie = Categorie::with('produits')->findOrFail( $id );

load()

La méthode load() permet d'effectuer la seconde requête plus tard. On parlera alors de « lazy eager loading ».

Ceci sera utile, par exemple, si les données de la relation ne sont utiles que dans un contexte donné.

Contrôleur Laravel (PHP)

$categories = Categorie::get();

 

...

 

if (...)  {

    $categories->load('produits');

 

    foreach ($categories as $categorie) {

        echo $categorie->produits->first()->prix;

    }

}

Pour toujours charger une relation hâtivement

Si vous avez besoin de charger la relation à chaque requête, il est possible d'ajouter une propriété with directement dans le modèle.

La valeur de cette propriété est un tableau qui contient le nom des relations à charger automatiquement.

Ex :

Modèle Laravel (PHP)

class Categorie extends Model

{

    ...

 

    /**
     * Relations à chargement hâtif automatique (eager load).
     *
     * @var array
     */
    protected $with = ['produits'];

 

    /**

     * Une catégorie peut avoir plusieurs produits.

     *

     * @return \Illuminate\Database\Eloquent\Relations\HasMany

     */

    public function produits() : HasMany

    {

        return $this->hasMany('App\Produit');

    }

    ...

}

Le chargement hâtif sera alors automatique avec une requête du genre :

Ex :

Contrôleur Laravel (PHP)

$categories = Categorie::get();

Il sera tout de même possible d'effectuer une requête sans charger la relation en utilisant without().

Ex :

Contrôleur Laravel (PHP)

$categories = Categorie::without('produits')->get();

Pour plus d'information

« Optimize Eloquent Queries with Eager Loading ». Laravel News. https://laravel-news.com/eloquent-eager-loading

« Laravel Eloquent: Eager Load Pivot Relations ». Medium. https://medium.com/@ajcastro29/laravel-eloquent-eager-load-pivot-relations-dba579f3fd3a

« Finding N+1 Queries in Laravel ». Marcel Pociot. https://pociot.dev/1-finding-n1-queries-in-laravel

▼Publicité

Veuillez noter que le contenu de cette fiche vous est partagé à titre gracieux, au meilleur de mes connaissances et sans aucune garantie.
Merci de partager !
Soumettre