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(): void10 {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 Seeder13{14 public function run(): void15 {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 :
-
$class
: le Seeder à exécuter. -
$silent
: un booléen qui, s'il est à true, désactive l'affichage des logs dans la console lors de l'exécution du Seeder. -
$parameters
: un tableau associatif de 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(): static19 {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(): static19 {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 Seeder12{13 public function run($count = 1): void14 {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 Seeder12{13 public function run($count = 1): void14 {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 Seeder12{13 public function run($count = 1): void14 {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
Utiliser une API pour nourrir une base de manière cohérente
Implémentation d’une alternative à l’utilisation de Faker pour disposer de données cohérentes.
William Suppo
Traquer un utilisateur dans les logs
Laravel offre la possibilité d'ajouter un contexte unique à chaque ligne de log, voyons comment utiliser cette feature pour traquer les erreurs d'un utilisateur !
Mathieu De Gracia
Automatisez l'insertion d'utilisateurs avec les Seeders
Explorons à nouveau l'utilisation des Seeders ! Pour bien maîtriser les fondamentaux, nous allons automatiser l'ajout de nouveaux utilisateurs dans trois contextes d'exemple.
Antoine Benevaut