Qu’est-ce qu’une factory
Pour les tests et le seeding, il faut insérer des enregistrements en base.
Plutôt que d’indiquer manuellement chaque valeur, les factories de Laravel permettent de définir un jeu d’attributs par défaut pour chaque modèle Eloquent.
Toute nouvelle application Laravel inclut database/factories/UserFactory.php.
namespace Database\Factories ;
use Illuminate\Database\Eloquent\Factories\ Factory ;
use Illuminate\Support\Facades\ Hash ;
use Illuminate\Support\ Str ;
class UserFactory extends Factory
{
// Mise en cache pour ne pas re-hacher le même mot de passe à chaque fois
protected static ? string $password ;
public function definition () : array
{
return [
'name' => fake () -> name (),
'email' => fake () -> unique () -> safeEmail (),
'email_verified_at' => now (),
'password' => static :: $password ??= Hash :: make ( 'password' ),
'remember_token' => Str :: random ( 10 ),
];
}
}
Le helper fake() donne accès à Faker pour générer diverses données aléatoires.
La locale de Faker se change via l’option faker_locale de config/app.php.
Créer une factory
Utilisez la commande Artisan make:factory.
php artisan make:factory PostFactory
Le fichier est créé dans database/factories.
Détection automatique modèle / factory
Si votre modèle utilise le trait HasFactory, Laravel trouve automatiquement la factory correspondante.
Il cherche dans le namespace Database\Factories une classe nommée modèle + Factory.
Si le nommage diffère, spécifiez explicitement avec l’attribut UseFactory.
use Illuminate\Database\Eloquent\Attributes\ UseFactory ;
use Database\Factories\Administration\ FlightFactory ;
#[ UseFactory ( FlightFactory :: class )]
class Flight extends Model
{
// ...
}
Définir une factory
Méthode definition()
Le cœur de la factory est la méthode definition(), qui retourne les attributs par défaut.
namespace Database\Factories ;
use Illuminate\Database\Eloquent\Factories\ Factory ;
class PostFactory extends Factory
{
public function definition () : array
{
return [
'user_id' => \App\Models\ User :: factory (),
'title' => fake () -> sentence (),
'content' => fake () -> paragraphs ( 3 , true ),
'published_at' => fake () -> optional () -> dateTimeBetween ( '-1 year' , 'now' ),
];
}
}
Méthodes Faker courantes :
Méthode Exemple généré fake()->name()John Doefake()->email()[email protected] fake()->sentence()Phrase aléatoire fake()->paragraph()Paragraphe aléatoire fake()->numberBetween(1, 100)Entier entre 1 et 100 fake()->dateTime()Date/heure aléatoire fake()->boolean()true / false
États de factory
Les méthodes d’état définissent des variantes.
use Illuminate\Database\Eloquent\Factories\ Factory ;
class UserFactory extends Factory
{
public function definition () : array
{
return [
'name' => fake () -> name (),
'email' => fake () -> unique () -> safeEmail (),
'account_status' => 'active' ,
];
}
// État utilisateur suspendu
public function suspended () : static
{
return $this -> state ( fn ( array $attributes ) => [
'account_status' => 'suspended' ,
]);
}
// État administrateur
public function admin () : static
{
return $this -> state ( fn ( array $attributes ) => [
'is_admin' => true ,
]);
}
}
Les états peuvent se combiner.
$user = User :: factory () -> suspended () -> create ();
$admin = User :: factory () -> admin () -> create ();
État soft delete
Pour les modèles à suppression logique, l’état trashed() intégré est disponible.
$user = User :: factory () -> trashed () -> create ();
Callbacks de factory
afterMaking et afterCreating permettent d’ajouter des traitements après la création du modèle.
namespace Database\Factories ;
use App\Models\ User ;
use Illuminate\Database\Eloquent\Factories\ Factory ;
class UserFactory extends Factory
{
public function configure () : static
{
return $this -> afterMaking ( function ( User $user ) {
// Après make() (pas encore en base)
}) -> afterCreating ( function ( User $user ) {
// Après create() (déjà en base)
});
}
// ...
}
Vous pouvez aussi enregistrer un callback dans une méthode d’état.
public function withProfile () : static
{
return $this -> state ( fn ( array $attributes ) => [])
-> afterCreating ( function ( User $user ) {
$user -> profile () -> create ([
'bio' => fake () -> paragraph (),
]);
});
}
Créer des modèles
make() — Sans sauvegarde
use App\Models\ User ;
// 1 modèle
$user = User :: factory () -> make ();
// 3 modèles (Collection)
$users = User :: factory () -> count ( 3 ) -> make ();
create() — Avec sauvegarde en base
// 1 modèle enregistré
$user = User :: factory () -> create ();
// 3 modèles enregistrés
$users = User :: factory () -> count ( 3 ) -> create ();
Surcharge d’attributs
Un tableau passé à make() ou create() surcharge des attributs précis.
$user = User :: factory () -> make ([
'name' => 'Taro Yamada' ,
]);
$user = User :: factory () -> create ([
'name' => 'Hanako Suzuki' ,
'email' => '[email protected] ' ,
]);
Surcharge inline via state.
$user = User :: factory () -> state ([
'name' => 'Ichiro Tanaka' ,
]) -> make ();
Lors d’une création via factory, la protection contre l’assignation en masse est désactivée automatiquement.
Sequence
Pour alterner des attributs lors de la génération, utilisez Sequence.
use App\Models\ User ;
use Illuminate\Database\Eloquent\Factories\ Sequence ;
$users = User :: factory ()
-> count ( 10 )
-> state ( new Sequence (
[ 'admin' => 'Y' ],
[ 'admin' => 'N' ],
))
-> create ();
// 5 avec admin='Y', 5 avec admin='N'
Version plus concise avec sequence().
$users = User :: factory ()
-> count ( 2 )
-> sequence (
[ 'name' => 'Premier utilisateur' ],
[ 'name' => 'Deuxième utilisateur' ],
)
-> create ();
Génération dynamique via une closure :
use Illuminate\Database\Eloquent\Factories\ Sequence ;
$users = User :: factory ()
-> count ( 10 )
-> state ( new Sequence (
fn ( Sequence $sequence ) => [ 'name' => 'Utilisateur ' . $sequence -> index ],
))
-> create ();
Factories et relations
Has Many (un à plusieurs)
has() crée un modèle avec une relation 1-N.
use App\Models\ Post ;
use App\Models\ User ;
// Un User avec 3 Posts
$user = User :: factory ()
-> has ( Post :: factory () -> count ( 3 ))
-> create ();
Version plus courte avec les méthodes magiques (has + pluriel du nom de la relation).
$user = User :: factory ()
-> hasPosts ( 3 )
-> create ();
// Surcharge d'attributs
$user = User :: factory ()
-> hasPosts ( 3 , [ 'published' => false ])
-> create ();
Belongs To (relation inverse)
for() désigne le parent auquel appartient le modèle.
use App\Models\ Post ;
use App\Models\ User ;
// 3 Posts appartenant à un utilisateur
$posts = Post :: factory ()
-> count ( 3 )
-> for ( User :: factory () -> state ([ 'name' => 'Hanako Tanaka' ]))
-> create ();
// Utiliser un modèle existant
$user = User :: factory () -> create ();
$posts = Post :: factory () -> count ( 3 ) -> for ( $user ) -> create ();
Version magique :
$posts = Post :: factory ()
-> count ( 3 )
-> forUser ([ 'name' => 'Hanako Tanaka' ])
-> create ();
Many to Many
hasAttached() gère les relations N-N, avec attributs de table pivot.
use App\Models\ Role ;
use App\Models\ User ;
$user = User :: factory ()
-> hasAttached (
Role :: factory () -> count ( 3 ),
[ 'active' => true ] // Attributs sur la table pivot
)
-> create ();
Version magique :
$user = User :: factory ()
-> hasRoles ( 1 , [ 'name' => 'Editor' ])
-> create ();
Relations polymorphes
Utilisez has() ou les méthodes magiques comme pour 1-N.
use App\Models\ Post ;
$post = Post :: factory () -> hasComments ( 3 ) -> create ();
Pour morphTo, la méthode magique n’est pas disponible : nommez explicitement la relation dans for().
$comments = Comment :: factory () -> count ( 3 ) -> for (
Post :: factory (), 'commentable'
) -> create ();
Définir une relation dans la factory
Dans definition(), si vous affectez une factory à une clé étrangère, le parent est créé automatiquement.
class PostFactory extends Factory
{
public function definition () : array
{
return [
'user_id' => \App\Models\ User :: factory (),
'title' => fake () -> sentence (),
'content' => fake () -> paragraph (),
];
}
}
recycle() — Réutiliser un modèle existant
Pour réutiliser la même instance sur plusieurs relations, utilisez recycle().
// Réutiliser la même Airline pour Ticket et Flight
Ticket :: factory ()
-> recycle ( Airline :: factory () -> create ())
-> create ();
// Choisir aléatoirement dans une collection
$airlines = Airline :: factory () -> count ( 3 ) -> create ();
Ticket :: factory () -> count ( 10 ) -> recycle ( $airlines ) -> create ();
Utilisation dans un seeder
Appelez la factory depuis DatabaseSeeder ou une autre classe de seeder.
namespace Database\Seeders ;
use App\Models\ User ;
use Illuminate\Database\ Seeder ;
class DatabaseSeeder extends Seeder
{
public function run () : void
{
// 10 utilisateurs, chacun avec 3 publications
User :: factory ()
-> count ( 10 )
-> hasPosts ( 3 )
-> create ();
}
}
Exécution :
Utilisation dans les tests
Dans un test, combinez avec RefreshDatabase.
namespace Tests\Feature ;
use App\Models\ User ;
use App\Models\ Post ;
use Illuminate\Foundation\Testing\ RefreshDatabase ;
use Tests\ TestCase ;
class PostTest extends TestCase
{
use RefreshDatabase ;
public function test_user_can_view_their_posts () : void
{
$user = User :: factory () -> create ();
$posts = Post :: factory () -> count ( 3 ) -> for ( $user ) -> create ();
$response = $this -> actingAs ( $user )
-> get ( '/dashboard' );
$response -> assertOk ();
$response -> assertSee ( $posts -> first () -> title );
}
public function test_suspended_user_cannot_post () : void
{
$user = User :: factory () -> suspended () -> create ();
$response = $this -> actingAs ( $user )
-> post ( '/posts' , [ 'title' => 'Test' ]);
$response -> assertForbidden ();
}
}
RefreshDatabase réinitialise la base entre les tests, évitant les interférences.
L’API des factories est identique avec Pest.
Utilisez uses(RefreshDatabase::class) pour réinitialiser la base.
Prochaines étapes
Seeding de base de données Voyez comment combiner seeders et factories pour alimenter votre base.
Introduction aux tests Découvrez la fonctionnalité de test de Laravel et le trait RefreshDatabase.