Laravel Eloquent : comprendre les modèles et leurs relations

Laravel est un framework très populaire, tellement populaire qu’il a quasiment éclipsé l’ensemble ou presque des autres concurrents basés sur PHP. Bien évidemment, il reste notre Symfony national sur lequel Laravel s’appuie en grande partie, mais il est un peu plus difficile à approcher. Je ne suis pas ici pour vous faire un comparatif entre les deux, mais bien pour vous parler d’une fonctionnalité importante de Laravel.

Dans cet article, nous allons parler des modèles qui vous permettent de gérer vos data dans la base de données. En effet, Laravel inclut Eloquent, un mappeur objet-relationnel (ORM) qui rend agréable l’interaction avec votre base de données. Lorsque vous utilisez Eloquent dans Laravel, chaque table de base de données a un « modèle » correspondant qui est utilisé pour interagir avec cette table.

En plus de récupérer des enregistrements de la table de la base de données, les modèles Eloquent vous permettent d’insérer, de mettre à jour et de supprimer des enregistrements de la table. Vous allez alors pouvoir utiliser des fonctions directement prévues et gérées par Laravel qui vous permettent de vous simplifier fortement la vie.

Parallèlement à cela, il y a ce que l’on appelle les relations entre vos modèles. En effet, une table peut être liée à une autre. Une fois que votre application se complexifie, vous allez alors avoir plusieurs tables qui seront liées entre elles selon ce que fait votre application. Une table  » commande  » sera liée à la table « produits « , une table  » client  » sera liée à une table  » commandes « , mais aussi une table  » factures  » elle-même liée à la table  » commandes « . Vous voyez l’idée ?

Dans ce tutoriel qui j’espère sera clair pour vous, je vais essayer de vous montrer tout cela en prenant l’exemple d’un blog simple fait sous le framework Laravel. Nous allons voir les migrations correspondantes pour l’exemple et surtout les modèles ainsi que les relations qui permettent de gérer un blog simple.

Les différents types de relation entre les modèles de données dans Laravel

hasOne : Cette relation indique qu’un enregistrement dans la table A ne peut être associé qu’à un seul enregistrement dans la table B. Il s’agit d’une relation « un à un » entre deux modèles de données. Ainsi la relation hasOne peut être décrite comme une association de type 1:1. Par exemple :

  • Un utilisateur ne peut avoir qu’un seul profil.
  • Une voiture n’a qu’une seule plaque d’immatriculation.
  • Un commentaire n’a qu’un seul auteur.

hasMany : Cette relation indique qu’un enregistrement dans la table A peut être associé à plusieurs enregistrements dans la table B. La relation hasMany peut être décrite comme une association de type 1:n. Par exemple :

  • Un article de blog peut avoir plusieurs commentaires.
  • Un sondage peu avoir plusieurs propositions.
  • Une commande ou un panier peut avoir plusieurs articles.

belongsTo : Cette relation indique qu’un enregistrement dans la table A appartient à un enregistrement unique dans la table B. La relation belongsTo peut également être décrite comme une association de type 1:n. Quelques exemples :

  • Un commentaire n’appartient qu’à un seul article.
  • Un article n’est écrit que par un seul auteur.
  • Une image n’a été upload que par un seul utilisateur.

Pour mieux comprendre la différence entre les deux dernières relations hasMany et belongsTo. on peut schématiser les choses de la manière suivante :

  • La relation hasMany est utilisée lorsqu’un modèle est en relation avec plusieurs instances d’un autre modèle. Par exemple, un utilisateur peut avoir plusieurs commentaires, ou une entreprise peut avoir plusieurs employés. Dans ce cas, le modèle qui a plusieurs instances est considéré comme le modèle parent et le modèle qui a une seule instance est considéré comme le modèle enfant. Ainsi, la relation hasMany est définie dans le modèle parent en utilisant la méthode hasMany.
  • La relation belongsTo est utilisée lorsqu’un modèle est en relation avec une seule instance d’un autre modèle. Donc un commentaire peut être associé qu’à un seul utilisateur, ou un employé peut être associé qu’à une seule entreprise. Dans ce cas, le modèle qui a une seule instance est considéré comme le modèle parent et le modèle qui a plusieurs instances est considéré comme le modèle enfant. La relation belongsTo est définie dans le modèle enfant en utilisant la méthode belongs. On peut considérer que c’est la fonction  » inverse  » qui vient compléter notre hasMany que nous venons de voir au dessus.

belongsToMany : Cette nouvelle relation indique qu’un enregistrement dans la table A peut être associé à plusieurs enregistrements dans la table B, et vice versa. Cette relation est souvent utilisée pour représenter des relations de type  » many-to-many « . On peut donc aussi indiquer que la relation belongsToMany peut être décrite comme une association de type n:m. Quelques exemples :

  • Plusieurs catégories peuvent être associées à plusieurs articles de blog.
  • Des produits peuvent être dans plusieurs commandes.
  • Des utilisateurs ont des langages de programmation préférés sur leur profil.
  • Des fables mentionnent des animaux.

hasManyThrough : Cette relation permet de traverser une relation belongsTo pour récupérer des enregistrements de la table B en passant par la table intermédiaire C. Avec notre exemple de blog simple mentionné au début, on veut donc par exemple récupérer tous les commentaires d’un utilisateur. Pour cela on peut utiliser la relation hasManyThrough en passant par la table des articles. Nous verrons dans le code juste après comment mettre ça en pratique.

Dit autrement, la relation hasManyThrough peut être décrite comme une association de type 1:n, mais en passant par une table de plus. Il faut donc imaginer qu’un enregistrement dans une table A peut être associé à plusieurs enregistrements dans une table B en passant par une table C.

hasOneThrough : Cette relation est similaire à hasManyThrough, sauf qu’elle permet de récupérer un enregistrement unique de la table B plutôt que plusieurs enregistrements. Par exemple, pour récupérer le profil d’un utilisateur en passant par la table des utilisateurs, on peut utiliser la relation hasOneThrough. Ainsi un enregistrement dans une table A est associé à exactement un enregistrement dans une table B en passant par une table C.

Le code pour comprendre les relations entre les modèles dans Laravel

Comme je vous l’ai dit dans l’introduction de cet article, pour illustrer tout ce que nous venons de voir, je vais illustrer ces différentes relations avec les modèles via un exemple de blog simple. Dans notre mini blog, il y a donc des articles, des commentaires sur les articles et enfin des auteurs qui écrivent des articles et des commentaires. On commence comme d’habitude par créer le cadre qu’il nous faut pour cela. Dans votre projet Laravel, vous pouvez donc créer les 3 migrations suivantes :

php artisan make:migration create_articles_table --create=articles
php artisan make:migration create_authors_table --create=authors
php artisan make:migration create_comments_table --create=comments

Normalement, si vous êtes arrivés ici, c’est que vous connaissez déjà un peu les migrations. On va maintenant remplir les 3 migrations que nous venons de créer avec le contenu qui va suivre.

La migration create_articles_table :

<?php

use Illuminate\Support\Facades\Schema;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Database\Migrations\Migration;

class create_articles_table extends Migration
{
    public function up()
    {
        Schema::create('articles', function (Blueprint $table) {
            $table->id();
            $table->string('title');
            $table->text('body');
            $table->timestamps();
        });
    }

    public function down()
    {
        Schema::dropIfExists('articles');
    }
}

La migration create_authors_table :

<?php

use Illuminate\Support\Facades\Schema;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Database\Migrations\Migration;

class create_authors_table extends Migration
{
    public function up()
    {
        Schema::create('authors', function (Blueprint $table) {
            $table->id();
            $table->string('name');
            $table->string('email');
            $table->timestamps();
        });
    }

    public function down()
    {
        Schema::dropIfExists('authors');
    }
}

La migration create_comments_table :

<?php

use Illuminate\Support\Facades\Schema;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Database\Migrations\Migration;

class create_comments_table extends Migration
{
    public function up()
    {
        Schema::create('comments', function (Blueprint $table) {
            $table->id();
            $table->integer('article_id');
            $table->integer('author_id');
            $table->text('body');
            $table->timestamps();
        });
    }

    public function down()
    {
        Schema::dropIfExists('comments');
    }
}

Maintenant que nous avons le contenu de nos migrations qui est fait, nous pouvons les jouer avec la commande :

php artisan migrate 

Introduction aux modèles de données dans Laravel

Bien évidemment, tout ce qui est décrit dans cet article est donné à titre d’exemple pour comprendre ce que sont les modèles et leur concept. Si vous faites un blog, je doute que vous utilisiez cette logique de données, mais je trouvais l’exemple facile à comprendre pour un nouveau venu dans Laravel et pour utiliser ce que le framework propose. Il est temps de passer à la partie la plus intéressante, les modèles de données.

On va donc créer nos 3 modèles correspondants à ce que nous venons de décrire dans les migrations avec les 3 commandes :

php artisan make:model Article
php artisan make:model Author
php artisan make:model Comment

Commencons par remplir le modèle de données Article, son contenu est alors le suivant :

<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\HasMany;
use Illuminate\Database\Eloquent\Relations\BelongsTo;

class Article extends Model
{
    public function comments(): HasMany
    {
        return $this->hasMany(Comment::class);
    }

    public function author(): BelongsTo
    {
        return $this->belongsTo(Author::class);
    }

    // suite du model
}

Dans ce modèle Article j’ai volontairement appliqué un typage fort pour respecter les bonnes pratiques du langage PHP et éviter les problèmes dans le futur. De plus, cela permet à votre IDE (environnement de développement intégré) de mieux comprendre ce qu’il se passe.

J’ai également ajouté un use statement pour chaque classe de relation que nous utilisons dans ce modèle. Cela permet à PHP d’importer automatiquement les classes nécessaires au moment de l’exécution, ce qui nous évite d’avoir à spécifier le chemin complet à chaque fois que nous voulons utiliser une classe de relation.

Enfin, concernant le modèle Article en lui-même, j’ai ajouté la mention comments() avec HasMany car si vous avez bien suivi au début, cette relation nous indique qu’un article de notre blog peut avoir plusieurs commentaires. Par contre, j’ai ensuite choisi de définir une relation « belongsTo » vers le modèle « Author » car un article n’a qu’un seul auteur. Passons au modèle suivant qui est Comment :

<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;

class Comment extends Model
{
    public function article(): BelongsTo
    {
        return $this->belongsTo(Article::class);
    }

    public function author(): BelongsTo
    {
        return $this->belongsTo(Author::class);
    }

    // ...
}

J’ai donc indiqué avec mes relations belongsTo qu’un commentaire n’appartient qu’à un seul article, et qu’il ne peut avoir qu’un seul auteur, pratique non ? À présent, on peut passer au dernier modèle qui permet de compléter le tout. Notre dernier modèle sera comme vous le devinez Author :

<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\HasMany;

class Author extends Model
{
    public function articles(): HasMany
    {
        return $this->hasMany(Article::class);
    }

    public function comments(): HasMany
    {
        return $this->hasMany(Comment::class);
    }

    // suite du model
}

En conclusion avec ce modèle dans Laravel, on indique via les relations dans notre modèle de donnée qu’un auteur peut donc avoir plusieurs articles et bien évidemment plusieurs commentaires. J’espère que ce petit exemple vous sera utile. Ce n’est pas pour autant terminé, car nous n’avons pas encore vu toutes les relations. Il nous manque ainsi les relations hasOne et belongsToMany, voyons voir comment on peut faire pour les introduire dans notre petit projet de blog.

Pour la suite, je ne vais pas vous fournir de migrations et les commandes de création du modèle, car maintenant, vous savez faire tout ca. On recommence avec hasOne. Pour l’intégrer à notre petit blog de test, on pourrait ajouter un profil à nos auteurs. En effet, chaque profil n’appartient qu’à un seul auteur et il ne peut pas avoir plusieurs profils, ce serait bizarre non ? On va alors ajouter un nouveau modèle Profile avec le contenu suivant :

<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;

class Profile extends Model
{
    public function author(): BelongsTo
    {
        return $this->belongsTo(Author::class);
    }

    // suite du model
}

Pour finir l’intégration de ce nouveau modèle, nous allons ajouter la mention concernant le profil dans le modèle de notre auteur. On modifie donc notre modèle Author avec cette nouvelle relation en plus du reste :

class Author extends Model
{
// code des précédentes relations
    public function profile()
    {
        return $this->hasOne(Profile::class);
    }

}

Bon, on y est presque pour le tour des relations ! Si vous suivez toujours, il nous manque une petite dernière que nous allons illustrer facilement. Pour introduire la relation belongsToMany dans notre super blog, on pourrait ajouter des catégories pour nos articles. Vous avez maintenant l’habitude, on crée donc le modèle Category :

<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsToMany;

class Category extends Model
{
    public function articles(): BelongsToMany
    {
        return $this->belongsToMany(Article::class);
    }
}

La fonction articles() retourne une relation de type BelongsToMany, elle permet de définir une relation de  » plusieurs à plusieurs  » entre les articles et les catégories. Il nous faut maintenant modifier notre modèle Articles pour terminer le travail. Pour cela, on va donc ajouter la relation suivante dans le modèle Articles :

class Article extends Model
{
    // précédentes relations

    public function categories(): BelongsToMany
    {
        return $this->belongsToMany(Category::class);
    }

    // suite du model
}

Et voilà ! Nous avons ajouté la fonction categories() qui retourne une relation de type BelongsToMany pour spécifier que chaque article peut appartenir à plusieurs catégories, et chaque catégorie peut être associée à plusieurs articles.

C’est déjà assez long, si vous avez des questions, n’hésitez pas à les poster dans les commentaires, j’y répondrai avec plaisir ! Dans le prochain article, nous verrons comment stocker et exploiter les data de notre base de données via les modèles.

Laisser un commentaire