21 octobre 2021 172.608K

Formation Laravel : Les relations

Dans l’exemple de notre chapitre précédent nous avons enregistré des posts contenant un champ en relation avec une table étrangère, les id de la table users. Nous avons donc vu comment enregistrer un nouvel user et récupérer son id lors de l’enregistrement même d’un post.

Mais cela ne nous donne qu’une relation 1:1 en résultat. Un seul utilisateur est associé à un seul post. Or nous aimerions associer un utilisateur avec plusieurs posts, on parle donc d’une relation 1:n (1 user pour n posts).

1. Les relations 1:n

Nous allons poursuivre notre exemple d’enregistrement de données avec nos Seeders et nos Factories et créer par exemple 2 users et chacun d’eux aura 10 posts.

Pour se faire notre Model User va rentrer en action. Nous allons devoir lui préciser qu’un utilisateur est susceptible d’avoir plusieurs posts. Voici comment la déclaration de la relation se présente dans notre Model User :

public function posts() {
    return $this->hasMany(Post::class);
}

Maintenant que nous avons déclaré cette méthode posts() dans notre Model nous allons pouvoir nous en servir dans notre UsersTableSeeder :

<?php

use Illuminate\Database\Seeder;
use Illuminate\Support\Str;

use App\Http\Models\User;
use App\Http\Models\Post;

class UsersTableSeeder extends Seeder
{
    /**
    * Run the database seeds.
    *
    * @return void
    */
    public function run()
    {
        factory(User::class, 2)->create()->each(function($user) {
            $user->posts()->saveMany(factory(Post::class, 10)->create([
                'user_id' => $user->id
            ]));
        });
    }
}

Petits explications sur le dérouler de ce code :

Premièrement on crée 2 enregistrements de la class User avec la fonction factory() chainée par la méthode create(), jusquee là rien de nouveau. Puis on y chaine une deuxième méthode each() qui prend en paramètre une fonction qui sera exécutée pour chaque User créé. Dans cette fonction on y passe en paramètre $user qui est un objet de la class User créé suite à la méthode create précédente.

Dans cette fonction on applique à $user la méthode que l’on vient de créer posts() , puis la méthode saveMany() qui prend en paramètre la création des enregistrements des articles.

La construction est donc exactement la même que nous avons déjà vu : une fonction factory() qui prend en paramètre une instance de la class Post et le nombre d’enregistrement souhaité, chainée par la méthode create.

À la seule particularité qu’ici nous passons un tableau dans cette fonction create() afin de surcharger la valeur 'user_id' qui était, je vous le rappelle, égale à ceci dans notre PostFactory :

'user_id'       => factory(User::class)->create()->id,

Or nous ne voulons pas créer un autre utilisateur ici, nous voulons prendre l’id de l’utilisateur qui vient d’être créer et qui est la variable $user donc on surcharge cette valeur directement dans la fonction create() par :

'user_id' => $user->id

Vous voyez ? Ce n’est vraiment pas sorcier !

2. Les relations n:n

Maintenant voyons la dernière relation qu’il nous reste à voir, la relation n:n. Pour illustrer par l’exemple nous allons créer des tags (des “étiquettes”) pour nos articles. Nous aurons donc forcément plusieurs tags pour plusieurs articles.

Commençons donc par créer la table et le model pour les tags :

php artisan make:model Tag -m
CLI Laravel - php artisan make:migration - Create migration

On modélise la table tags dans la migration créée :

<?php

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

class CreateTagsTable extends Migration{
    /**
    * Run the migrations.
    *
    * @return void
    */
    public function up()
    {
        Schema::create('tags', function (Blueprint $table) 
        {
            $table->increments('id');
            $table->char('title', 255);

            $table->timestamps();
        });
    }

    /**
    * Reverse the migrations.
    *
    * @return void
    */
    public function down()
    {
        Schema::dropIfExists('tags');
    }
}

On précise ensuite dans les models Post et Tag leur relation n:n grâce à la méthode belongsToMany() que l’on place dans une méthode de leur model respectif.

// dans le model Tag
public function posts() {
    return $this->belongsToMany('App\Http\Models\Post');
}
// dans le model Post
public function tags() {
    return $this->belongsToMany('App\Http\Models\Tag');
}

Ensuite on crée la table de relation post_tag

php artisan make:migration create_post_tag_table

Dans la convention de nommage des tables de la base de données Laravel nomme les tables de relation au singulier. Vous pouvez cependant les nommer comme bon vous semble en précisant le nom souhaité en deuxième paramètre de la méthode belongsToMany() des 2 models :

public function tags() {
    return $this->belongsToMany('App\Http\Models\Tag', 'nom_de_ma_table');
}

Maintenant on modélise la table post_tag avec ses 2 clés étrangère 'user_id' et 'post_id' :

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

class CreatePostTagTable extends Migration
{    
    /**     
    * 
    Run the migrations.     
    *     
    * @return void     
    */    
    public function up()    
    {        
        Schema::create('post_tag', function (Blueprint $table) {            
            $table->increments('id');
            $table->char('title', 255);
            $table->unsignedInteger('post_id');
            $table->foreign('post_id')
                    ->references('id')
                    ->on('posts');
                    
            $table->unsignedInteger('tag_id');
            $table->foreign('tag_id')
                    ->references('id')
                    ->on('tags');

            $table->timestamps();
        });
    }

    /**
    * Reverse the migrations.
    *
    * @return void
    */
    public function down()
    {
        Schema::dropIfExists('post_tag');
        Schema::table('posts', function (Blueprint $table) {
            $table->dropColumn('post_id');
            $table->dropForeign(['post_id']);
        });
        Schema::table('tags', function (Blueprint $table) {
            $table->dropColumn('tag_id');
            $table->dropForeign(['tag_id']);
        });
    }
}

On en profite pour vider la base de données en même temps que d’envoyer nos nouvelles migrations :

php artisan migrate:fresh

Maintenant on la “seed” !

On a donc besoin de créer notre Seeder et notre Factory pour la nouvelle table tags :

php artisan make:seeder TagsTableSeeder
php artisan make:factory TagFactory

On définit rapidement TagFactory :

use Faker\Generator as Faker;
use App\Http\Models\Tag;

$factory->define(Model::class, function (Faker $faker) 
{
    return [
    'title' => $faker->word
    ];
});

Puis son TagsTableSeeder, pour l’exemple on crée 15 tags :

public function run()
{
    factory(Tag::class, 15)->create();
}

Et dans DatabaseSeeder on lui dit de lancer TagsTableSeeder en premier :

public function run()
{
    $this->call([
        TagsTableSeeder::class,
        UsersTableSeeder::class
    ]);
}

Maintenant quand on créer x posts pour chaque user on veut que chacun de ces posts ait divers tags existant. Retournons donc dans notre UsersTableSeeder et rajoutons la relation entre posts et tags :

public function run()
{
    factory(User::class, 2)->create()->each(function($user) {
        $user->posts()->saveMany(factory(Post::class, 10))->create()->each(function($post) {
            $post->tags()->attach([
                rand(1, 5),
                rand(6, 10),
                rand(11, 15)
            ]);
        });
    });
}

On précise simplement que pour chaque post créé create()->each() on veut qu’une fonction soit appliquée. Dans cette fonction on passe l’objet $post qui est un objet de la class Post qui vient d’être créé et on lui attribut 3 tags grâce à la méthode tags() que nous venons de créer plus tôt.

Voilà nous venons de finir cette partie sur la gestion et l’interaction de la Base de Données avec Laravel. Après c’est 2 grandes parties pleines de théorie je vous invite maintenant à vous rendre dans la partie 3 qui sera un TP qui vous permettra de mettre en pratique tout ce que l’on vient de voir ensemble. J’en profiterai également pour enrichir au mieux votre apprentissage en vous montrant d’autres choses 😉

Besoin d'une formation personalisée ?

Avis

4,9
Rated 4.9 out of 5
Excellent88%
Splendide !12%
Sympa0%
Sans plus0%
Pas terrible0%

Pas de titre

Rated 5 out of 5
15 avril 2024

cool

paul

Pas de titre

Rated 5 out of 5
2 avril 2024
Evelyn

Pas de titre

Rated 4 out of 5
2 avril 2024

Merci beaucoup pour ce cour.

adil

Pas de titre

Rated 5 out of 5
7 mars 2024

super

hajar

Pas de titre

Rated 5 out of 5
21 juin 2023

Merci énormément je suis sur exciter à cause de ce cours

Kassi Patrick