Soit une relation définie comme suit :
Ex :
class Categorie extends Model
{
...
/**
* Une catégorie peut avoir plusieurs produits.
*
* @return IlluminateDatabaseEloquentRelationsHasMany
*/
public function produits()
{
return $this->hasMany('AppProduit');
}
...
}
Il existe des différences importantes entre l'utilisation d'une méthode de relation (ex : $produits = $categorie->produits()->get();) et l'utlisation d'une propriété dynamique (ex : $produits = $categorie->produits;).
Pour choisir la bonne technique, vous devez vous demander de quel type de données vous avez besoin pour effectuer le traitement souhaité.
Si vous utilisez une méthode qui définit une relation (ex : produits()), vous obtiendrez un objet de relation, c'est-à-dire un objet dont la classe est dérivée de la classe Relation (ex : HasMany, BelongsTo, BelongsToMany, HasOne, HasManyThrough, etc.). Ces classes contiennent toutes une propriété de type Illuminate\Database\Eloquent\Builder.
Il sera donc possible de continuer à monter la requête après avoir appelé la méthode définissant la relation.
Vous devrez terminer le tout par une méthode terminale qui se chargera d'exécuter la requête SQL générée (ex : get(), pluck()).
$produits = $categorie->produits()->orderBy('prix')->get();
Si vous tentez de continuer la requête alors que vous n'avez pas en main un objet qui le permet, vous obtiendrez un message du genre « BadMethodCallException in Macroable.php line 74: Method orderBy does not exist. ».
Ex :

Parfois, vous aurez besoin de travailler avec une instance du modèle.
Il faut savoir que les propriétés dynamiques retourneront une instance du modèle si la relation permet d'obtenir 0 ou 1 enregistrement. Ce sera le cas par exemple pour les relations belongsTo() et hasOne().
Le fait d'avoir en main une instance du modèle permet d'accéder aux membres de ce modèle, comme les champs et les relations.
Par exemple, si le modèle Produit contient une méthode categorie() qui définit une relation belongsTo() avec le modèle Categorie, on pourra retrouver la description de la catégorie comme suit :
$descriptionCategorie = $produit->categorie->description;
Il sera même possible d'enchaîner les relations. Dans cet exemple, le modèle Categorie contient une méthode famille() qui définit une relation belongsTo() avec le modèle Famille.
$descriptionFamille = $produit->categorie->famille->description;
Pour obtenir le même résultat avec la méthode de relation, il aurait fallu procéder comme suit :
$descriptionFamille = $produit->categorie()->first()->famille->description;
En effet, même si la relation ne peut pas retrouver plus d'un enregitrement, get() retournera une collection. Il faut donc user d'astuce en utilisant first().
Attention : si vous tentez d'utiliser une relation alors que vous avez en main une collection, vous obtiendrez l'erreur « Undefined property: IlluminateDatabaseEloquentCollection::$factures. ».
Ex :

D'autres fois, vous aurez besoin de travailler avec une collection d'instances du modèle.
Lorsque la relation permet d'obtenir 0, 1 ou plusieurs enregistrements, les propriétés dynamiques retourneront une collection dont chaque élément est une instance du modèle. La collection pourrait être vide, contenir un seul élément ou contenir plusieurs éléments. Il n'en reste pas moins qu'il s'agira d'une collection.
Le fait d'avoir en main une collection d'instance du modèle permet d'utiliser les méthodes de manipulation de collections.
$factures = $categorie->produits->count();
On obtiendra exactement le même résultat avec ceci :
$factures = $categorie->produits()->get()->count();
Fait intéressant, l'utilisation de count() fonctionnera également si on l'applique à un objet de relation qui, rappelons-le, contient une propriété de type Illuminate\Database\Eloquent\Builder :
$factures = $categorie->produits()->count();
Ceci s'explique par le fait qu'il existe une méthode count() dans la classe Illuminate\Support\Collection de même que dans la classe Builder.
Il existe d'autres méthodes qui peuvent être appliquées autant à une collection qu'à un objet de relation. Par exemple : first(), pluck(), etc.
Quand vous avez le choix entre travailler sur une collection ou sur un objet de relation, vous obtiendrez généralement de meilleures performances si vous appliquez la méthode sur un objet de relation.
En effet, la requête SQL ne sera exécutée que lorsque Laravel rencontrera une méthode terminale. Vous avez donc tout le loisir de monter la requête pour qu'elle soit optimale avant de la lancer.
L'illustration devient claire avec first(). Si vous l'appliquez à une collection, la requête SQL ira chercher tous les enregistrements puis PHP ne retiendra que le premier élément :
$factures = $categorie->produits->first();
Si vous travaillez avec un objet de relation, la requête SQL n'ira chercher que le premier enregistrement, ce qui nécessitera moins de ressources :
$factures = $categorie->produits()->first();
Attention : si vous tentez d'utiliser une méthode de manipulation de collection qui n'a pas son équivalent dans Builder ou qui attend des paramètres différents, vous obtiendrez une erreur du genre « BadMethodCallException in Builder.php line 2450: Call to undefined method Illuminate\Database\Query\Builder::except(). ».
Ex :
$factureExclue = 2;

« Welcome to Lesson Twelve of Eloquent by Example! ». Eloquent by example. http://eloquentbyexample.com/course/lesson/lesson-12-relationship-queries
▼Publicité