Formation PUB030 : Laravel, 2019 Accéder aux données d'une relation

23.6 Quand doit-on utiliser les méthodes définissant les relations vs les propriétés dynamiques?


Soit une relation définie comme suit :

Ex :

Modèle Laravel (PHP)

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é.

Builder

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()).

Contrôleur Laravel (PHP)

$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 :

Contrôleur Laravel (PHP)

$produits = $categorie->produits->orderBy('prix')->get();

BadMethodCallException in Macroable.php line 74: Method orderBy does not exist.

Instance du modèle

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 :

Contrôleur Laravel (PHP)

$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.

Contrôleur Laravel (PHP)

$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 :

Contrôleur Laravel (PHP)

$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 :

Contrôleur Laravel (PHP)

$facturesDeLaCategorie = $categorie->produits->factures;

Undefined property: IlluminateDatabaseEloquentCollection::$factures.

Collection d'instances du modèle

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.

Contrôleur Laravel (PHP)

$factures = $categorie->produits->count();

On obtiendra exactement le même résultat avec ceci :

Contrôleur Laravel (PHP)

$factures = $categorie->produits()->get()->count();

Méthodes portant le même nom dans Collection et dans Builder

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 :

Contrôleur Laravel (PHP)

$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 :

Contrôleur Laravel (PHP)

$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 :

Contrôleur Laravel (PHP)

$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 :

Contrôleur Laravel (PHP)

$factureExclue = 2;

$factures = $categorie->produits()->except($factureExclue);

BadMethodCallException in Builder.php line 2450: Call to undefined method IlluminateDatabaseQueryBuilder::except().

Pour plus d'information

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

▼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