Vous souhaitez nous soutenir ? Devenez sponsor de l'association sur notre page Github
Tutoriels

Automatisez l'insertion d'utilisateurs avec les Seeders

Antoine Benevaut avatar
Publié le 20 novembre 2024
Couverture de l'article Automatisez l'insertion d'utilisateurs avec les Seeders

Plongeons à nouveau dans l'utilisation des Seeders !

Pour acquérir les bases avec les Seeders, nous allons automatiser l'insertion de nouveaux utilisateurs dans trois contextes d'exemples.

Ces contextes utiliseront le model User.php et son fichier de migration qui sont fournis à la création d'un nouveau projet Laravel.

Nous limiterons cet article à une utilisation en environnement "local", j'entends par là que nous allons créer des utilisateurs qui vous permettront de faire vos tests manuels sur votre application ou encore des démonstrations.

L'article ne traitera pas de l'utilisation des Seeders dans les tests, mais la limite étant relativement fine, vous pourriez facilement vous servir de ce qui suit pour créer une base de données de test et la mettre en œuvre avec la documentation.

Passons à l'action !

Voici le scénario que l'on souhaite tester.

Notre application doit permettre à un utilisateur de créer un compte sur notre site, ce sera un nouvel utilisateur ou newUser. Ensuite, notre utilisateur devra valider son courriel, il sera alors prêt pour être onboardé et nous l'appellerons onboardableUser. Enfin, notre utilisateur fera son onboarding en remplissant son profile, il sera ainsi un onboardedUser.

Nous ne verrons pas dans cet article le détail du code qui fera fonctionner cette application, notre focus sera uniquement fait sur les seeders. Laissez votre imagination vous guider si vous souhaitez la développer, tout ceci est réalisable en suivant la documentation Laravel.

Concentrons-nous sur DatabaseSeeder.php et sur nos trois premiers Seeders NewUserTestSeeder, OnboardableUserTestSeeder et OnboardedUserTestSeeder.

Et pour commencer, nous alors mettre en place un garde-fou ; Souvenez-vous, la commande php artisan db:seed execute le fichier DatabaseSeeder.php par défaut.

Charge à vous de décider comment vous souhaitez utiliser ce fichier DatabaseSeeder.php, pour cet article, je vous propose de ne l'utiliser que pour construire votre base de donnée pour les environnements local et testing (deux des trois environnements par défaut de Laravel).

Nous allons donc commencer par empêcher l'exécution de ce Seeder si l'environnement de votre application est production.

1<?php
2 
3namespace Database\Seeders;
4 
5use Illuminate\Database\Seeder;
6 
7class DatabaseSeeder extends Seeder
8{
9 public function run(): void
10 {
11 if (app()->environment('production')) {
12 throw new \Exception('Do not run DatabaseSeeder on production');
13 }
14 }
15}

Nous voilà protégé, impossible de détruire notre base de production en exécutant la commande php artisan db:seed volontairement ou involontairement.

Passons à nos Seeders, nous avons déjà défini leurs noms, ajoutons les à notre DatabaseSeeder.

1<?php
2 
3namespace Database\Seeders;
4 
5use Database\Seeders{
6 NewUserTestSeeder,
7 OnboardableUserTestSeeder,
8 OnboardedUserTestSeeder
9};
10use Illuminate\Database\Seeder;
11 
12class DatabaseSeeder extends Seeder
13{
14 public function run(): void
15 {
16 if (app()->environment('production')) {
17 throw new \Exception('Do not run DatabaseSeeder on production');
18 }
19 
20 $this
21 ->call(NewUserTestSeeder::class, false, ['count' => 10])
22 ->call(OnboardableUserTestSeeder::class, false, ['count' => 10])
23 ->call(OnboardedUserTestSeeder::class, false, ['count' => 10])
24 ;
25 }
26}

Pour nos trois Seeders, nous allons faire des fichiers distincts et nous les appelons avec la méthode $this->call(). Séparer les Seeders va nous faciliter la compréhension sur leur responsabilité. Avec un nom éloquent, nous traduirons immédiatement le service rendu par le Seeder et nous mettons en avant un cas réel de notre application.

La méthode call() accepte trois paramètres :

Maintenant que nous savons comment appeler nos Seeders, passons à leur création !

Nous allons nous baser sur les Factories de modèles pour gagner du temps et faciliter la réutilisation de ces Factories dans d'autres contextes, comme les tests fonctionnels, par exemple. Si vous n'êtes pas encore familier avec ce concept, consultez la documentation de Laravel pour en savoir plus.

Pour débuter, nous utiliserons la class UserFactory disponible à l'installation du framework.

1<?php
2 
3class UserFactory extends Factory
4{
5 protected static ?string $password;
6 
7 public function definition(): array
8 {
9 return [
10 'name' => fake()->name(),
11 'email' => fake()->unique()->safeEmail(),
12 'email_verified_at' => now(),
13 'password' => static::$password ??= Hash::make('password'),
14 'remember_token' => Str::random(10),
15 ];
16 }
17 
18 public function unverified(): static
19 {
20 return $this->state(fn (array $attributes) => [
21 'email_verified_at' => null,
22 ]);
23 }
24}

Cette Factory définit les colonnes de la table via la méthode definition(). Elle inclut également une méthode unverified() qui permet de définir la colonne email_verified_at à null. Cette colonne indique la date de validation de l'email de l'utilisateur. Notez aussi que le mot de passe de cet utilisateur sera par défaut "password".

Étant donné que nous utilisons le schéma de la table users fourni par Laravel, il n'existe pas de colonne dédiée pour indiquer le contexte de l'utilisateur. Pour déterminer ce contexte (tel que newUser, onboardableUser ou onboardedUser), nous nous basons sur des conditions appliquées à une ou plusieurs colonnes existantes.

Cette approche nous sera utile par la suite pour identifier un compte utilisateur dans un contexte spécifique. Je vous propose d'ajouter directement cette information dans le champ email en utilisant un préfixe, qui sera généré par la Factory.

Ajoutons une méthode emailWithPrefix() à notre Factory :

1<?php
2 
3class UserFactory extends Factory
4{
5 protected static ?string $password;
6 
7 public function definition(): array
8 {
9 return [
10 'name' => fake()->name(),
11 'email' => fake()->unique()->safeEmail(),
12 'email_verified_at' => now(),
13 'password' => static::$password ??= Hash::make('password'),
14 'remember_token' => Str::random(10),
15 ];
16 }
17 
18 public function unverified(): static
19 {
20 return $this->state(fn (array $attributes) => [
21 'email_verified_at' => null,
22 ]);
23 }
24 
25 public function emailWithPrefix(string $prefix)
26 {
27 return $this->state(fn (array $attributes) => [
28 'email' => "{$prefix}-" .fake()->unique()->safeEmail(),
29 ]);
30 }
31}

Contexte 1: newUser

Un utilisateur vient de créer son compte.

1<?php
2 
3namespace App\Domain\Users\Seeders;
4 
5use App\Domain\Users\Users\User;
6use Illuminate\Database\Seeder;
7 
8/**
9 * php artisan db:seed --class="NewUserTestSeeder"
10 */
11class NewUserTestSeeder extends Seeder
12{
13 public function run($count = 1): void
14 {
15 if (app()->environment('production')) {
16 throw new \Exception('Do not run NewUserTestSeeder on production');
17 }
18 
19 User::factory()
20 ->count($count)
21 ->unverified()
22 ->emailWithPrefix('new-user')
23 ->create();
24 }
25}

Nous pouvons maintenant créer un newUser en exécutant la commande :

1php artisan db:seed --class="NewUserTestSeeder"

Ce seeder (et les prochains) sera appelé par notre DatabaseSeeder avec le paramètre count à 10, on récupère notre argument $count pour générer N utilisateurs "newUser".

Le courriel de l'utilisateur n'est pas confirmé, on admet qu'il doit recevoir et/ou consulter le mail de bienvenue.

L'e-mail de cet utilisateur commencera avec le prefix "new-user". Dans la base de données, on verra donc N utilisateurs avec un e-mail "new-user-fake@mail.com". Nous pourrons alors utiliser un de ces utilisateurs avec le mot de passe "password" pour nous connecter à ce compte dans le contexte "newUser".

Contexte 2: onboardableUser

Un utilisateur a validé son courriel, il est prêt pour remplir son profil.

1<?php
2 
3namespace App\Domain\Users\Seeders;
4 
5use App\Domain\Users\Users\User;
6use Illuminate\Database\Seeder;
7 
8/**
9 * php artisan db:seed --class="OnboardableUserTestSeeder"
10 */
11class OnboardableUserTestSeeder extends Seeder
12{
13 public function run($count = 1): void
14 {
15 if (app()->environment('production')) {
16 throw new \Exception('Do not run OnboardableUserTestSeeder on production');
17 }
18 
19 User::factory()
20 ->count($count)
21 ->emailWithPrefix('onboardable')
22 ->create();
23 }
24}

Cette fois-ci, le prefix change pour "onboardable" et l'utilisateur aura son e-mail validé.

Contexte 3: onboardedUser

Un utilisateur a validé son courriel et a rempli son profil, trop fort ! C'est le compte d'un utilisateur actif sur notre application !

1<?php
2 
3namespace App\Domain\Users\Seeders;
4 
5use App\Domain\Users\Users\User;
6use Illuminate\Database\Seeder;
7 
8/**
9 * php artisan db:seed --class="OnboardedUserTestSeeder"
10 */
11class OnboardedUserTestSeeder extends Seeder
12{
13 public function run($count = 1): void
14 {
15 if (app()->environment('production')) {
16 throw new \Exception('Do not run OnboardedUserTestSeeder on production');
17 }
18 
19 User::factory()
20 ->count($count)
21 ->emailWithPrefix('onboarded')
22 ->has(Profile::factory())
23 ->create();
24 }
25}

Cette fois-ci, le prefix change pour "onboarded", l'utilisateur aura son e-mail validé !


Ça y est ! Nous avons le DatabaseSeeder qui nous permet une lecture facile de nos cas métiers et nos 3 Seeders qui présentent ces cas en détails.

Maintenant, si l'on exécute la commande php artisan db:seed, j'ai 30 comptes utilisateurs qui vont se créer, 10 pour chaque contexte utilisateur. Ces comptes sont à votre disposition sur votre environnement.

En local, vous pourrez les utiliser pour vos tests. Et si vous avez un environnement de tests pour votre PO ou client, il pourra lui aussi tester les cas fonctionnels manuellement.

Notez bien que dans ces exemples, nous créons des comptes en masses, chaque utilisateur aura un mail unique et différent à chaque génération.

Vous pourriez avoir besoin de créer un compte avec un mail fix, pour votre propre compte par exemple, cela peut être un bon entrainement pour prendre le sujet en mains 😉

A lire

Autres articles de la même catégorie