Écrit par Alexandre Buleté

Mise à jour le 20 juillet 2019 | Difficulté : moyen

5.0/5   1 Avis  |  2.9K views

Seeders et Factories

Dans le chapitre précédent nous avions dû créer un utilisateur et des articles pour nos tests. Nous les avions créer directement à partir du Controller et vous avez pu constater que ce n'est pas très pratique. Surtout si les données à rentrer deviennent de plus en plus complexes et que nous sommes amenés à en enregistrer fréquemment lors de la phase de développement de notre application pour divers test.

Heureusement Laravel nous permet de rentrer des enregistrements de manière beaucoup plus "propre" mais surtout beaucoup plus rapide et réutilisable grâce aux Seeders et aux Factories ! Nous allons voir que les deux travaillent ensemble et qu'ils portent bien leurs noms !

1. Les Seeders

Dans l'absolu il est possible de n'utiliser que les Seeders pour peupler notre base de données de manière très basique. Car leur action est bien de "seed" ("ensemencer" en anglais) notre base de données.

Dans notre exemple, les deux tables (posts et users) ont une relation "user_id" et pour gérer au mieux cette relation lors de notre seeding les Factories ont un rôle encore plus important. Donc suivez bien jusqu'à la fin ;)
Ici nous allons voir très rapidement comment créer et gérer vos seeders.

Pour créer un nouveau seeder pour notre table posts lancez la commande :


php artisan make:seeder PostsTableSeeder

Ce nouveau fichier se trouvera dans le dossier /database/seeds 

Arborescence des Seeders - Laravel

Les protections de "Mass Assignment" que nous avons vu dans le chapitre précédent (avec les tableaux $fillable et $guarded) sont automatiquement désactivées lors de l'execution d'un seeder.

Ici je crée un seeder spécialement pour la table 'posts' même si j'aurais pu écrire le code que nous devrons lancer dans le fichier de base DatabaseSeeder.php. Je préfère être bien organisé dès le début. Mais pour que notre seeder PostsTableSeeder s'execute nous devons utiliser la méthode call dans la fonction run() de notre class DatabaseSeeder comme ceci :


use Illuminate\Database\Seeder;

class DatabaseSeeder extends Seeder
{
    /**
    * Seed the application's database.
    *
    * @return void
    */
    public function run()
    {
        $this->call([
            // UsersTableSeeder::class,
            PostsTableSeeder::class
        ]);
    }
}

On voit que la ligne appellant le seeder UserTableSeeder est commentée, il n'existe pas encore donc nous allons le créer puis décommenter cette ligne. Car souvenez-vous que nous avons besoin d'une clé étrangère 'user_id' de la table users. Donc nous devons enregistrer des utilisateurs dans notre base de données avant de pouvoir y enregistrer des articles.


php artisan make:seeder UsersTableSeeder

Puis décommentez la ligne :


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

Rendez-vous ensuite dans notre UsersTableSeeder et précisez les infos à rentrer dans la table lors de sont enregistrement dans la base de données.


use Illuminate\Database\Seeder;
use Illuminate\Support\Str;
    
class UsersTableSeeder extends Seeder{
    /**
    * Run the database seeds.
    *
    * @return void
    */
    public function run()
    {
        DB::table('users')->insert([
            'name' => Str::random(10),
            'email' => Str::random(10).'@gmail.com',
            'password' => bcrypt('secret')
        ]);
    }
}

Vous voyez que nous ne rentrons pas toutes les colonnes, en effet pas besoin de rentrer les infos des colonnes ayant une valeur NULL par défaut à moins que vous n'en ayez besoin pour vos tests.

Maintenant au tour de PostsTableSeeder :


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

class PostsTableSeeder extends Seeder{
    /**
    * Run the database seeds.
    *
    * @return void
    */
    public function run()
    {
        DB::table('posts')->insert([
            'title'         => Str::random(10),
            'description'   => Str::random(30),
            'content'       => Str::random(1048),
            'user_id'       => 1
        ]);
    }
}

Ici la valeur 'user_id' est à 1, ce qui signifie que nous allons prendre en relation le premier enregistrement de la table users.

Pour finir nous allons seeder la base de données mais avant cela assurer vous d'effacer correctement tous vos enregistrements déjà présents pour repartir d'une base vide :


php artisan migrate:fresh

Assurez-vous de n'avoir aucune donnée dans votre base et lancez la commande de seeding : 


php artisan db:seed

Si vous avez bien suivi vous devriez voir dans votre base de données un nouvel enregistrement dans la table users et posts.

Ok c'est bien, mais si je veux plusieurs users, chacun ayant plusieurs posts ? 

En effet, cette simple méthode montre très vite ses limites lorsque l'on veut complexifier nos enregistrements. C'est pourquoi les Factories vont nous être très utiles.

2. Les Factories

Les Factories ("usines" en anglais) sont donc là pour nous permettre de créer des enregistrements en quantité et d'établir facilement diverses relations entre nos tables. Ça tombe bien c'est notre cas ! :)

Vos Factories se situe dans le dossier /database/factories.

Arborescence fichiers Factories - Laravel

On peut voir que notre UserFactory est déjà créé par défaut. Nous allons donc créer le PostFactory


php artisan make:factory PostFactory --model=Post

L'option --model de la commande permet de préciser de quel model il s'agit.

Voici ce que contient le fichier PostFactory

À noter qu'ici je remplace le chemin du Model car comme je vous l'ai précisé dans les chapitres précédents je préfère placer mes Models dans app/Http/Models.

<?php

use Faker\Generator as Faker;

use App\Http\Models\Post;

$factory->define(Post::class, function (Faker $faker) {
  return [
    //
  ];
});

Pour comprendre un peu mieux comment se construit un Factory ouvrez le UserFactory déjà existant : 


<?php

use Faker\Generator as Faker;

use App\Http\Models\User;

$factory->define(User::class, function (Faker $faker) {
    return [
        'name' => $faker->name,
        'email' => $faker->unique()->safeEmail,
        'email_verified_at' => now(),
        'password' => '$2y$10$TKh8H1.PfQx37YgCzwiKb.KjNyWgaHb9cbcoQgdIVFlYg7B77UdFm', // secret
        'remember_token' => str_random(10),
    ];
});

On peut voir ici que notre UserFactory renvoie un tableau avec les valeurs par défaut à rentrer pour notre model User. Et grâce à une fonction prenant en paramètre une instance de la Class Faker (alias Faker\Generator).

Faker est une librairie PHP créé par François Zaninotto nous permettant ici de générer diverses valeurs aléatoires pour nos différents champs. Pour en apprendre plus sur tout ce dont il est possible de faire avec cette librairie PHP je vous invite à vous rendre sur le github du repository Faker de François Zaninotto.

La méthode safeEmail vous permet de définir les emails avec le nom de domaine example.org, example.com ou example.net vous assurant ainsi de ne pas entrer par hasard une éventuelle adresse mail existante.

Définissons les valeurs de nos champs dans notre PostFactory :


<?php

use Faker\Generator as Faker;
use App\Http\Models\User;
use App\Http\Models\Post;

$factory->define(Post::class, function (Faker $faker) {
    return [
        'title'         => $faker->sentence,
        'description'   => $fake->paragraph(1),
        'content'       => implode($faker->paragraphs(10)),
        'user_id'       => factory(User::class)->create()->id,
    ];
});

On reprend la même logique que notre UserFactory en utilisant de nouveau une instance de Faker et grâce à ces diverses méthodes on prédéfinit les champs à remplir.

Ici je préfère utiliser la méthode paragraphs pour le contenu car il me permet d'avoir plus de texte. Vous pouvez utiliser la méthode text mais bien que le nombre max de caractères sont modifiable, le nombre minimum est de 5. J'ai donc plus de chance d'avoir un contenu long avec paragraphs. Cette méthode paragraphs retourne un tableau donc je place tout ça dans la fonction PHP implode() et le tour est joué. ;)

Intéressons nous maintenant au champ user_id.

La valeur de celui-ci doit être un ID existant dans la table users. Il nous faut donc créer un user et récupérer son id. Pour cela nous utilisons la fonction helper factory(), on lui précise en paramètre de quel Model il s'agit, puis on lui demande de le créer d'après les paramètres définit dans UserFactory et enfin on récupère son id.

Maintenant que vos factory sont définis rendez-vous dans votre fichier PostsTableSeeder :


public function run()
{
    factory(Post::class, 10)->create();
}

ici nous voulons simplement créer 10 posts donc pas besoin de UsersTableSeeder, commentez la ligne correspondant à l'appel du Seeder des users dans votre fichier DatabaseSeeder :


$this->call([
    // UsersTableSeeder::class
    PostsTableSeeder::class
]);

Maintenant effacez à nouveau les enregistrements présent dans votre base de données et envoyez vos nouveaux enregistrements. Voici une commande plus rapide qui combine le migrate:fresh et le db:seed


php artisan migrate:fresh --seed

Vous vous retrouvez maintenant avec 10 nouveaux posts chacun en relation avec un user différent. Ce que l'on pourrait nommer de relation 1:1, pour un user il existe un post.

Dans le chapitre suivant nous allons voir 2 autres types de relation, les relations 1:n et les relations n:n.

Avis

N'hésitez pas à laisser votre avis. Actuellement 1 avis sur ce cours

Note Globale : 5.0/5

Classer par …  

Date

Rate

Dans l'ordre…  

Croissant

Décroissant

5.0/5

Merci beaucoup.

il y a 1 mois

notez et laissez un avis

--/5

connectez-vous pour laisser un avis

Vous n'avez pas de compte ? Inscrivez-vous ici.