Article mis à jour le 20 février 2022 avec la nouvelle version Laravel 9
Laravel Eloquent est l’une des fonctionnalités phares du framework Laravel. C’est en partie grâce à son support impressionnant pour définir, créer et gérer les relations entre différentes tables de données. Dans ce tutoriel, je vais vous montrer comment créer et utiliser les relations Eloquent, afin que vous puissiez être opérationnel sans aucune connaissance préalable des relations, je vous invite également à lire pour tutoriel Laravel 9 pour les débutants qui offre une autre approche pour apprendre Laravel avec Eloquent.
Que sont les relations dans les bases de données ?
Tout d’abord, commençons par les bases absolues. Qu’est-ce qu’une relation exactement ? Une relation signifie que vous avez deux tables ou plus avec des enregistrements qui sont liés les uns aux autres. Disons que vous avez un users
et que chaque utilisateur peut avoir plusieurs posts
. Comment connecter ces deux éléments ? Habituellement, cela se fait en ajoutant un user_id
à la colonne posts
de sorte que vous puissiez facilement identifier l’utilisateur qui appartient à chaque message. Cette « connexion » qui vous permet de déterminer quels enregistrements appartiennent les uns aux autres est appelé une relation.
Quels types de relations Eloquent existent ?
Il existe plusieurs types de relations Eloquent. Le type que vous avez dépend généralement du fait que vous avez un ou (potentiellement) plusieurs éléments à la fois de la première et de la seconde table.
Dans l’exemple ci-dessus, nous avons un users
et une table posts
table. Disons que chaque message ne peut appartenir qu’à un utilisateur. Pour mettre cela en œuvre, nous pouvons ajouter un user_id
à l’élément posts
à la table posts
. Et dans le scénario opposé (plus improbable), si chaque message peut appartenir à plusieurs auteurs (users
) et que chaque auteur ne peut contribuer qu’à un seul message, nous pourrions ajouter un fichier post_id
à la table users
. Il s’agit d’un exemple de relation relativement simple, une relation de type One-To-Many relation.
Mais que se passe-t-il si chaque utilisateur peut avoir plusieurs messages, et si chaque message peut avoir plusieurs auteurs. Comment pouvons-nous résoudre cela ? C’est ce que l’on appelle une relation Many-To-Many. Pour ce type de relation plus complexe, nous ne pouvons pas simplement ajouter une colonne à un tableau et en finir ! Pour cette situation, nous avons besoin d’une table d’association. Ne vous inquiétez pas si cela vous semble trop complexe, nous y reviendrons bientôt.
En général, on dit qu’il existe les types de relations suivantes entre les données :
- Relation de type « à un » ou « un à un »
- Relation de type « un à plusieurs » (One-To-Many)
- Has-One-Of-Many (par exemple, la relation dernier parmi tant d’autres) Relations
- Relation HasOneThrough et HasManyThrough
- Relation Many-To-Many
Relations individuelles avec Eloquent
Commençons par le relation la plus simple. Une relation de type « Has One » ou « One-To-One ». Cela signifie effectivement qu’un certain enregistrement est lié à un autre enregistrement (mais pas à plusieurs autres enregistrements !).
Continuons l’exemple du blog avec les utilisateurs et les messages, disons que chaque utilisateur peut avoir un profil. Dans certaines situations, vous pourriez stocker toutes les informations de profil dans le modèle Utilisateur, mais ce ne serait pas parfait. Ici, dans notre exemple, je veux les stocker dans une table séparée. Par exemple, si nous voulons plus tard transférer un profil à un utilisateur différent, cela sera pratique.
D’abord, créez un modèle Profile
, le User
est déjà généré par défaut. Les colonnes exactes pour le modèle Profile
n’ont pas beaucoup d’importance, mais vous pourriez vouloir créer un modèle et la migration à côté:
php artisan make:model Profile -m
Comme il s’agit d’une relation individuelle, nous avons la rare opportunité de décider si.. :
- nous plaçons un
user_id
sur leProfile
o - nous ajoutons un
profile_id
sur leUser
modèle.
Vous pouvez choisir ce que vous voulez, mais considérez que la relation parle d’un User
qui a un Profile
. Et si vous parlez de quelque chose qui a quelque chose d’autre, vous ajoutez généralement cette colonne au deuxième modèle. On pourrait dire que le premier modèle « possède » le second modèle.
C’est donc ce que je vais faire :
/** Ajoutez ceci à votre migration de table de profil ou créez une nouvelle migration */
$table->foreignId('user_id');
Lancer la migrations et nous sommes prêts à commencer à implémenter ceci.
D’abord, ouvrez votre User
et ajoutez les fonction publique suivants. Vous êtes autorisé à appeler la fonction comme vous le souhaitez, mais la convention est la suivante snake_case
:
public function profile()
{
return $this->hasOne(Profile::class);
}
Le hasOne
s’attend maintenant à ce que la fonction profile() utilise la colonne user_id
du modèle Profile
. Si le nom de votre colonne est différent, ajoutez un deuxième argument à l’instruction hasOne
avec l’autre nom de colonne :
return $this->hasOne(Profile::class, 'author_id');
Cependant, il n’est pas recommandé de choisir autre chose que la convention.
Maintenant que vous avez réussi à ajouter votre fière relation Eloquent! Faites défiler la page pour apprendre à l’utiliser.
En ajoutant l’inverse de cette relation
Dans la plupart des cas, nous pouvons aussi définir l’inverse d’une relation. Cela semble complexe, mais ça ne l’est pas vraiment. En gros, cela signifie que nous ajoutons maintenant une fonction sur le modèle Profile
qui renvoie au modèle auquel il appartient (User
dans ce cas), dans votre modèle Profile :
public function user()
{
return $this->belongsTo(User::class, 'author_id');
}
Utilisation d’une relation Eloquent
Utilisation de cette Relation éloquente de type One-To-One est très simple. Chaque fois que vous avez une instance de l’objet User
vous pouvez appeler $user->profile
ou $user->profile()->enchainerDautreMethodeIci()
.
C’est parce que $user
est une instance de la classe User
et, sur ce modèle, nous avons ajouté une méthode relationelle profile()
.
Veuillez noter, et c’est très important, qu’avec cette méthode vous pouvez accéder à la fois à ->profile
comme s’il s’agissait d’une propriété et ->profile()
comme s’il s’agissait d’une fonction. La différence est que ->profile
renvoie juste une instance d’Eloquent, alors que ->profile()
vous permet d’y ajouter des méthodes. Par exemple ->profile()->orderBy('xxx', 'ASC')->get()
.
En bref, ->profile
est un raccourci pour ->profile()->get()
.
$user = auth()->user();
$profile = $user->profile;
$name = $user->profile->display_name;
// Créer un profil
$profile = $user->profile()->create([
//
]);
$user = Profile::find(1)->user;
Relation éloquente de type One-To-Many
Une autre relation très importante, peut-être même la plus importante, est la relation Relation un à plusieurs. Également connue sous le nom de hasMany-relationship, cette relation définit une relation qui un élément a de nombreux autres éléments. Cette relation est très similaire à la précédente.
Pour continuer notre exemple de blog, disons qu’un profil a plusieurs postes. Ouvrez votre Profile
et ajoutez la méthode suivante :
public function posts()
{
return $this->hasMany(Post::class);
//Ou return $this->hasMany(Post::class, 'foreign_key');
}
Cela signifie en fait que chaque profil a de nombreux messages. L’inverse existe aussi :
public function profile()
{
return $this->belongsTo(Profile::class);
//Ou return $this->belongsTo(Profile::class, 'foreign_key');
}
Son utilisation est très similaire à celle que nous avons montrée plus haut, sauf que ce La relation renvoie plusieurs éléments (sous forme de collection Eloquent).
Maintenant que vous avez défini la relation en tant que fonction sur le modèle, nous pouvons à nouveau l’utiliser comme une fonction propriété (->posts
), qui renvoie un Eloquent collection ici, au lieu d’un seul modèle. Et nous pouvons également l’utiliser comme une fonction (->posts()->where('created_at', '>', now()->subDays(14))->get()
).
$posts = Profile::find(1)->posts;
//Ou $posts = Profile::find(1)->posts()->get();
foreach ($posts as post) {
// TODO
}
$lastPost = Profile::find(1)->posts()->latest()->first();
Ajout du modèle le plus récent/non récent et le plus ancien via une relation
Parfois, lorsque l’on définir une relation hasMany vous pourriez vouloir récupérer uniquement le les plus récents ou seulement le modèle le plus ancien. Pour ce faire, Laravel vous permet d’utiliser deux méthodes pratiques, ->latestOfMany()
et ->oldestOfMany()
:
public function latestPost()
{
return $this->hasMany(Post::class)->latestOfMany();
}
public function oldestPost()
{
return $this->hasMany(Post::class)->oldestOfMany();
}
Maintenant, ces deux méthodes ne renverront que un seul modèle au lieu d’une collection !
$latestPost = Profile::find(1)->latestPost;
Si cela vous semble bien, mais que vous avez besoin de filtres personnalisés ou autre avancé where()
clausesPour en savoir plus, consultez cet exemple dans la documentation de Laravel :
/**
* Obtenez le prix actuel du produit
*/
public function currentPricing()
{
return $this->hasOne(Price::class)->ofMany([
'published_at' => 'max',
'id' => 'max',
], function ($query) {
$query->where('published_at', '<', now());
});
}
HasOneThrough et HasManyThrough
Maintenant que nous avons vu ces exemples, allons un peu plus loin. Nous voulons maintenant définir une relation à travers un autre modèle.
Considérons notre exemple ci-dessus, où chaque User
a un Profile
et où chaque Profile
a de nombreux Post
s. Un exemple de relation à travers un autre modèle est que nous voulons obtenir tous les messages d’un utilisateur. Nous ne pouvons pas ajouter directement un hasMany(Post::class)
sur notre modèle User
, parce que le modèle Post
ne contient pas de user_id
. Il a seulement un profile_id
. C’est un exemple parfait de relation has many through.
Pour définir une telle relation, vous pouvez procéder de manière très similaire à ce que nous avons vu ci-dessus. Dans notre exemple, ajoutez ce qui suit au fichier User
modèle :
public function posts()
{
return $this->hasManyThrough(Post::class, Profile::class);
}
Le premier argument de cette fonction est le modèle auquel nous voulons accéder et le second argument est le modèle intermédiaire.
Définition d’une relation hasOneThough
est presque exactement la même chose qu’une hasMany()
bien que, dans cette situation, vous deviez vous assurer qu’il n’y a qu’un seul élément final :
public function first_login()
{
return $this->hasOneThrough(FirstLogin::class, Profile::class);
}
Relations plusieurs à plusieurs
Maintenant que nous avons vu le type de relations ci-dessus, nous allons aborder des relations plus avancées. Une relation plusieurs à plusieurs où chaque User
peut en avoir plusieurs Profile
par exemple lorsqu’on collabore avec d’autres personnes, et chaque Profile
peut avoir plusieurs User
.
Comment résoudre ce problème ? Nous ne pouvons pas simplement ajouter un profile_id
sur la table des utilisateurs, car il y a potentiellement plusieurs profils. Et nous ne pouvons pas non plus ajouter un user_id
sur le Profile
modèle, parce qu’il y a potentiellement plusieurs utilisateurs également.
Pour résoudre ce problème, nous avons besoin d’une sorte de table intermédiaire. Cette table intermédiaire est appelée table d’association, et dans la plupart des cas, il n’a d’autre fonction que de stocker deux id
dans la même rangée.
Considérons le scénario suivant : si notre table d’association a un user_id
et un profile_id
nous pouvons maintenant relier une certaine User
avec un certain Profile
. Et parce que nous pouvons avoir la même user_id
autant de fois que nous le souhaitons, nous pouvons connecter chaque utilisateur avec autant de profils que nous le souhaitons également. Et bien sûr, cela s’applique également aux profils : nous pouvons avoir autant de user_id
en double que nous le souhaitons. profile_id
que l’on veut, avec autant de profils différents que l’on veut. user_id
que l’on veut.
Comment créer une migration pour une table d’association Laravel ?
Tout d’abord, nous allons créer la migration de la base de données dont nous avons besoin. Exécutez la commande suivante. Remarquez comment le nom de la table est construit par en joignant les noms des tables (tous deux au singulier !) dans l’ordre alphabétique. Vous pouvez passer outre, mais cela n’a généralement pas d’intérêt.
php artisan make:migration create_profile_user_table --create=profile_user
Maintenant, ouvrez le fichier migration et ajouter deux lignes supplémentaires à la up()
méthode.
<?php
use IlluminateDatabaseMigrationsMigration;
use IlluminateDatabaseSchemaBlueprint;
use IlluminateSupportFacadesSchema;
class CreateProfileUserTable extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::create('profile_user', function (Blueprint $table) {
$table->id();
$table->timestamps();
$table->foreignId('profile_id');
$table->foreignId('user_id');
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::dropIfExists('profile_user');
}
}
Comme vous le voyez, la structure d’une table d’association est très simple. Maintenant, exécutez les migrations.
Ensuite, ouvrons nos deux modèles et ajoutons les deux fonctions de relation. Les méthodes sur chaque modèle sont toutes deux de la forme belongsToMany()
.
Ajoutez ceci à votre User
modèle :
/**
* Fonction publique pour obtenir tous les profils sous lesquels cet utilisateur peut publier
*/
public function profiles()
{
return $this->belongsToMany(Profile::class);
}
Et ceci pour votre Profile
modèle :
/**
* Fonction publique pour obtenir tous les utilisateurs qui peuvent publier sous ce profil
*/
public function users()
{
return $this->belongsToMany(User::class);
}
Si vous choisissez une autre table que le nom par défaut, vous pouvez ajouter votre propre nom de table en tant que paramètre à la méthode d’extraction. belongsToMany
fonctions
return $this->belongsToMany(Profile::class, 'user_role'); // The table here is in reverse alphabetical order
Par défaut, Eloquent suppose que leuser_id
et profile_id
colonnes du tableau croisé dynamique. Si vous choisissez de les nommer différemment, vous pouvez l’utiliser comme troisième et quatrième paramètre :
return $this->belongsToMany(Profile::class, 'user_role', 'nom_de_la_colonne_qui_represente_l_identifiant_de_ce_modele', 'nom_de_la_colonne_qui_represente_l_identifiant_de_la_cle_etrangere');
// Cela semble inutilement complexe, alors voici un exemple.
// Disons que nous ajoutons ceci sur le modèle utilisateur, par défaut cela ressemblerait à ceci :
return $this->belongsToMany(Profile::class, 'user_role', 'user_id', 'profile_id');
Utilisation d’une relation Eloquent Many-To-Many
L’utilisation de cette relation est très facile et sera similaire à ce que nous avons vu ci-dessus. Regardez ces petits exemples :
$user = User::find(1);
$user->profiles->each(function ($item) use ($user) {
$profile->name = $user->firstname . ' ' . $user->lastname;
$profile->save();
});
$profile = Profile::find(1);
$profile->users()->orderBy('created_at', 'desc');
Stockage des données dans le tableau croisé dynamique
La dernière partie de cet article porte sur le stockage des données dans la table d’association. Oui, c’est également possible ! Ce n’est peut-être pas nécessaire pour tous les cas d’utilisation, mais il y a certainement des cas où cela peut s’avérer utile.
Pour résumer, Qu’est-ce qu’une ligne dans une table d’association ? Que représente-t-elle ? La réponse est qu’une ligne dans un tableau croisé dynamique représente la relation entre deux enregistrements. Si nous devions supprimer une ligne spécifique, la relation existerait-elle toujours ? Non !
Voici pourquoi cela est important. Si chaque ligne d’un tableau croisé dynamique représente une relation, nous pouvons également stocker des informations sur la relation dans la table d’association. Il peut s’agir d’informations (relativement) triviales sur la date de création de la relation ou de sa dernière mise à jour (avec la fonction created_at
ou updated_at
colonnes).
Utilisation d’une table d’association pour récupérer des données de relation
Dans notre exemple ci-dessus, nous avons donné une migration pour le table d’association. Cette migration contenait déjà le code de notre created_at
et updated_at
(avec l’option $table->timestamps();
). Alors, comment accéder à cette relation ?
D’abord, nous devons le dire à Eloquent : notre modèle de pivot a également quelques autres attributs auxquels je veux accéder, les voici‘.
Vous pouvez faire ça comme ça :
/**
* Fonction publique pour obtenir tous les profils sous lesquels cet utilisateur peut publier
*/
public function profiles()
{
return $this->belongsToMany(Profile::class)->withPivot('created_at', 'updated_at', 'active');
}
Ou utiliser la méthode d’aide agréable ->withTimestamps()
pour définir déjà le created_at
et updated_at
colonnes :
/**
* Fonction publique pour obtenir tous les profils sous lesquels cet utilisateur peut publier
*/
public function profiles()
{
return $this->belongsToMany(Profile::class)->withTimestamps()->withPivot('active');
}
Vous pouvez maintenant accéder à la table d’association comme ceci :
$user = User::find(1);
foreach ($user->profiles as $profile) {
echo $profile->pivot->created_at;
}
Pratique, non ?
Renommer le nom de votre table d’association
Il y a aussi une autre astuce sympa que vous pouvez utiliser et c’est Renommer l’attribut pivot
. Eloquent utilise un langage descriptif et expressif. La documentation donne l’exemple d’une relation entre un podcast et un utilisateur. Cette relation est un abonnement, il serait donc un peu bizarre d’utiliser ‘pivot’ pour faire référence à un abonnement.
// Pas bien :
$user->podcasts()->first()->pivot->price;
// Bien :
$user->podcasts()->first()->subscription->price;
Pour renommer l’attribut pivot, vous pouvez utiliser la fonction ->as($name)
sur la fonction return $this->belongsToMany()
:
return $this->belongsToMany(Podcast::class)
->as('subscription')
->withTimestamps();
Conclusion
Wow, c’était un long article ! Si vous êtes en train de le lire, merci de vous joindre à moi ! Dans cet article, je vous ai parlé de tous les différents types de produits. types de relations entre les modèles Eloquent. J’espère que cela vous a été utile dans votre parcours d’apprentissage des relations Eloquent.
Les relations présentées dans cet article sont celles que vous utiliserez le plus. Et à mon avis, les relations ci-dessus sont plus que suffisantes pour le moment.
Néanmoins, vous pouvez rencontrer des situations où ces relations ne sont pas suffisantes. Ici, nous avons toujours parlé de la relation entre un modèle et un autre modèle. Mais considérez la situation relativement courante, où vous pouvez (par exemple) marquer à la fois un Post
et un Page
. Maintenant, les relations ci-dessus ne seront pas suffisantes, nous n’avons pas vu les relations polymorphes cependant j’ai déjà écris une Introduction aux relations polymorphes d’Eloquent, je vous invite à le lire pour approfondir le sujet