Damien Flandrin

Tutoriels, astuces et conseils sur le développement web par un développeur web

Utiliser efficacement Eloquent de Laravel

il y a 8 mois · 4 MIN DE LECTURE
#Laravel 

Depuis Laravel 5.3, j'ai vu de nombreux cas où les relations d'Eloquent et leurs constructeurs de requêtes sous-jacents ne sont pas utilisés efficacement. Beaucoup de ces cas existent parce qu'il y a plusieurs chemins d'accès aux mêmes données, dont beaucoup ressemblent à des solutions équivalentes. À moins que vous ne soyez familier avec ce qui se passe sous le capot, il n'est pas toujours clair quelle voie serait la meilleure.

La décision à prendre se situe généralement entre $blog->posts et $blog->posts().

La première ira dans la base de données, récupérera tous les messages liés au blog, et créera une collection de posts. Cela coûte cher car nous devons créer et hydrater une instance de chaque modèle associé.

Une bonne question à se poser est "Ai-je besoin d'une instance du modèle ?" Il est courant de n'avoir besoin de données que pour quelques attributs. Sauf si vous comptez sur les attributs casting ou les mutateurs ( les dates de Carbon par exemple), vous pouvez obtenir ces attributs directement à partir de la base de données.

$blog->posts() retournera un constructeur immédiatement, sans faire aucune requête pour le moment. La question à se poser ici est : "Ai-je besoin de toute la collection ?" La requête peut-elle être plus précise ? Une bonne pratique consiste à faire le plus possible dans la base de données pendant la requête et le moins possible en PHP, par exemple un endroit dans la base de données sera beaucoup plus rapide qu'un endroit sur une collection.

Voyons quelques exemples.

N'utilisez pas une collection pour compter le nombre de vos résultats

$blog->posts->count() créera une collection de modèles d'articles, avec tous leurs attributs, et retournera le nombre de modèles dans la collection. Si la relation posts est déjà chargée sur le modèle, ce sera en fait le chemin le plus rapide car vous n'aurez pas besoin de faire de requêtes du tout.

$blog->posts()->count()exécutera une seule requête d'agrégation et récupérera un entier de la base de données. Pas de modèles, pas d'attributs. Nous voulons juste que le nombre d'éléments soit un nombre entier, non ?

Les tests de performance indiquent que le deuxième est ~20 fois plus rapide pour une collection de 5 000 articles, mais cela sera différent sur d'autres environnements, il vaut donc la peine de le tester pour voir par vous-même. Notez que la différence deviendra plus extrême au fur et à mesure que la taille de la collection augmentera.

N'utilisez pas "where" sur la collection si vous pouvez le faire sur le constructeur.

$blog->posts->where('author', 1) créera d'abord tous les modèles, puis itérera à travers la collection un par un pour tester contre l'auteur, créant une nouvelle collection séparée, et finalement retournant le nombre d'articles dans cette collection. Tous les index que nous avons dans notre base de données sont inutiles parce que nous inspectons les modèles en PHP.

$blog->posts()->where('auteur', 1)->get() est entièrement executé dans la base de données, qui peut utiliser des index et ne retourne que les modèles qui correspondent au 'where', pour ne pas avoir à les hydrater tous quand on traite les résultats.

N'utilisez pas "pluck" sur la collection, utilisez plutôt le constructeur.

Les projets Laravel utilisent pluck partout. Nous voulons souvent juste la valeur d'une seule colonne, parfois saisie par une autre, par exemple une liste de noms d'utilisateur saisie par l'identifiant de l'utilisateur. Si nous utilisons notre exemple de blog, nous pourrions vouloir le nom de chaque message saisi par le post_id.

Une façon d'y parvenir est d'utiliser $blog->posts->pluck('name','id'), mais nous sommes en train de recréer une collection, et pour cela nous devons créer tous les modèles également. Nous n'avons pas besoin des modèles cependant, nous avons juste besoin d'une petite tranche de leurs données d'attributs, et nous pouvons utiliser le constructeur pour cela.

$blog->posts()->pluck('name','id') peut sembler très similaire mais le comportement sous le capot est très différent. L'appel d'arrachage passe au constructeur, qui fait un SELECT name, id sur la table du modèle, et construit le tableau en utilisant les résultats bruts de la base de données.

Comme mentionné précédemment, l'utilisation du constructeur n'appellera aucun des mutateurs du modèle, car il n'est tout simplement pas au courant du modèle Eloquent du tout. Dans l'exemple ci-dessus, si le nom était un attribut dynamique ou modifié lors de l'accès en utilisant getNameAttribute sur le modèle, la sélection sur le constructeur sera différente.

N'allez pas chercher toute la collection si vous n'avez besoin que du premier modèle.

Cela peut sembler une erreur évidente, mais j'en ai assez vu pour le mentionner. $blog->posts->first(), qui comme auparavant doit récupérer et hydrater toute une collection de modèles associés pour ne renvoyer que le premier et jeter le reste.

Si vous ne voulez que le premier modèle, vous devez utiliser le constructeur de requêtes de la relation : $blog->posts()->first(). Cela permet de régler les paramètres OFFSET et LIMIT appropriés lors de l'exécution de la requête, en ne renvoyant et en hydratant qu'un seul modèle.

Attendez, comment ça marche ? La clé pour comprendre ceci est de réaliser que certaines méthodes appelées sur la relation (ou le modèle) passent par le constructeur de requête via la méthode magique __call sur ces classes. Ainsi, le first() call sur la relation posts() finit en fait par appeler en first() sur un constructeur qui est créé en utilisant le nom de table du modèle ou du modèle associé.

Conclusion

Il y a beaucoup d'autres cas comme celui-ci où l'utilisation directe du constructeur peut faire une grande différence. Considérez toujours ce que le Laravel fait dans les coulisses, et si ce n'est pas clair, entrez dans le code source et suivez-le.

···

Damien Flandrin

Je suis développeur web pour l'agence Carredas, je fais principalement du PHP avec Laravel et Prestashop.

Développeur Web - Damien Flandrin