La méthode hasManyThrough() permet d'accéder directement aux données d'une table éloignée sans avoir à passer manuellement par la table intermédiaire. Attention : on ne parle pas ici de table pivot puisque cette table ne contient pas deux clés étrangères. Plutôt, elle contient la clé primaire d'une relation et la clé étrangère d'une autre relation.
La relation hasManyThrough() sera définie dans le modèle de la table principale de la première relation.
Par exemple, un pays peut regrouper plusieurs provinces et une province peut avoir plusieurs villes. Dans cet exemple, la table provinces contient la clé étrangère pour la première relation (pays_id) ainsi que la clé primaire de la seconde relation (id). On peut donc dire que la table provinces permet de faire un lien entre pays et villes.
La relation hasManyThrough() pourra être définie dans le modèle de la table pays, ce qui permettra d'accéder directement à l'ensemble des villes d'un pays, toutes provinces confondues.
La relation sera définie comme suit :
/**
* Un modeleA peut avoir plusieurs modeleB.
*
* @return \Illuminate\Database\Eloquent\Relations\HasManyThrough
*/
public function modelesB() : HasManyThrough
{
return $this->hasManyThrough('App\ModeleB', 'App\ModeleTableIntermediaire', 'nom_cle_etrangere_dans_table_intermediaire_vers_table_actuelle', 'nom_cle_etrangere_dans_table_eloignee_vers_table_intermediaire', 'nom_cle_primaire_dans_table_actuelle', 'nom_cle_primaire_dans_table_intermediaire');
}
Dans la plupart des cas, seuls les deux premiers paramètres seront obligatoires.
Puisque hasManyThrough() peut retrouver plus d'un enregistrement, il est d'usage de mettre le ou les mots définissant la relation au pluriel.
Ex :
class Pays extends Model
{
...
/**
* Un pays peut regrouper les villes de plusieurs provinces.
*
* @return \Illuminate\Database\Eloquent\Relations\HasManyThrough
*/
public function villes() : HasManyThrough
{
return $this->hasManyThrough('App\Ville', 'App\Province');
}
...
}
On précisera obligatoirement le nom du modèle le plus éloigné (ici : Ville) suivi du nom du modèle intermédiaire (ici : Province).
Par défaut, chacune des relations sera établie entre une clé primaire nommée id et une clé étrangère dont le nom est au format modele_id, c'est-à-dire que son nom est formé par le nom du modèle suivi de _id (ex : pays_id pour la clé étrangère vers la table actuelle et province_id pour la clé étrangère vers la table intermédiaire).
Si le nom des champs impliqués dans les relations sont différents, on pourra préciser les noms de champs à utiliser pour chacune des relations (optionnel dans cet exemple).
return $this->hasManyThrough('App\Ville', 'App\Province', 'pays_id', 'province_id');
Les informations de la table associée peuvent être retrouvées en appelant la méthode qui définit la relation ou encore à l'aide d'une propriété dynamique.
Peu importe la technique utilisée, Laravel montera la requête SQL avec la jointure nécessaire pour obtenir les données.
Ex :
$villes = $pays->villes()->get();
ou :
$villes = $pays->villes;
Si vous êtes intéressés à voir la requête générée par une relation hasManyThrough(), vous pouvez utiliser toSql().
Ex :
$villes = Pays::find(1)->villes()->toSql();
Un dump and die de $produits affichera ceci :
"select * from `villes` inner join `provinces` on `provinces`.`id` = `villes`.`province_id` where `provinces`.`pays_id` = ?"
Il n'y a pas de méthode permettant de définir la relation inverse d'un hasManyThrough().
Mais alors, serait-il possible de faire exactement l'inverse du hasManyThrough(), c'est-à-dire retrouver le pays d'une ville ? Pas de problème, Eloquent nous permettra d'y arriver facilement puisqu'une ville appartient à une seule province et qu'à partir de cette province, on peut retrouver le pays auquel elle appartient.
Ex :
$pays = Ville::find(1)->province->pays;
Remarquez que pour que cette requête fonctionne, il faut utiliser une propriété dynamique (nom que la méthode qui définit la relation, mais sans les parenthèses) sur le modèle du centre. En effet, la propriété dynamique (ici : province) retourne une instance du modèle (ici : une instance de Province) alors il est possible de la faire suivre par une méthode définie dans le modèle Province (ici : pays).
« HasManyThrough ». Laravel API. https://laravel.com/api/5.3/Illuminate/Database/Eloquent/Relations/HasManyThrough.html
« Eloquent: Relationships - Has Many Through ». Laravel. https://laravel.com/docs/master/eloquent-relationships#has-many-through
« Laravel – querying any level far relations with simple trick ». LSOFTonSOFA. https://softonsofa.com/laravel-querying-any-level-far-relations-with-simple-trick/
« Tweaking Eloquent relations – how to get hasMany relation count efficiently? ». LSOFTonSOFA. https://softonsofa.com/tweaking-eloquent-relations-how-to-get-hasmany-relation-count-efficiently/
▼Publicité