Saltar al contenido principal

Qué son las factorías

En los tests y en el seeding de la base de datos necesitas insertar registros. En lugar de indicar manualmente los valores de cada columna, con las factorías de modelos de Laravel puedes definir un conjunto de atributos por defecto para cada modelo Eloquent. Todas las nuevas aplicaciones Laravel incluyen 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
{
    // Cachea la misma contraseña para no rehash cada vez
    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),
        ];
    }
}
Con el helper fake() tienes acceso a la librería Faker, que genera todo tipo de datos aleatorios.
Puedes cambiar la localización de Faker con la opción faker_locale de config/app.php.

Generar una factoría

Crea una factoría con el comando Artisan make:factory.
php artisan make:factory PostFactory
La nueva clase se ubica en el directorio database/factories.

Detección automática de modelo y factoría

Si el modelo usa el trait HasFactory, Laravel localiza automáticamente su factoría. Busca dentro del namespace Database\Factories una clase cuyo nombre sea el del modelo con el sufijo Factory. Si la convención no coincide, indícalo explícitamente con el atributo UseFactory.
use Illuminate\Database\Eloquent\Attributes\UseFactory;
use Database\Factories\Administration\FlightFactory;

#[UseFactory(FlightFactory::class)]
class Flight extends Model
{
    // ...
}

Definir una factoría

Método definition()

Los atributos por defecto se definen en el método definition().
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étodos de Faker más habituales:
MétodoDato de ejemplo
fake()->name()John Doe
fake()->email()[email protected]
fake()->sentence()Frase aleatoria
fake()->paragraph()Párrafo aleatorio
fake()->numberBetween(1, 100)Entero entre 1 y 100
fake()->dateTime()Fecha y hora aleatoria
fake()->boolean()true / false

Estados de la factoría (states)

Los métodos de estado permiten definir variantes concretas para la factoría.
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',
        ];
    }

    // Estado de usuario suspendido
    public function suspended(): static
    {
        return $this->state(fn (array $attributes) => [
            'account_status' => 'suspended',
        ]);
    }

    // Estado de usuario administrador
    public function admin(): static
    {
        return $this->state(fn (array $attributes) => [
            'is_admin' => true,
        ]);
    }
}
Los estados se pueden combinar.
$user = User::factory()->suspended()->create();
$admin = User::factory()->admin()->create();

Estado de soft delete

En los modelos con soft delete dispones del estado integrado trashed().
$user = User::factory()->trashed()->create();

Callbacks de la factoría

Los métodos afterMaking y afterCreating te permiten ejecutar lógica adicional tras generar el modelo.
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) {
            // Se ejecuta tras make() (aún no guardado en BD)
        })->afterCreating(function (User $user) {
            // Se ejecuta tras create() (guardado en BD)
        });
    }

    // ...
}
También puedes registrar callbacks dentro de un método de estado.
public function withProfile(): static
{
    return $this->state(fn (array $attributes) => [])
        ->afterCreating(function (User $user) {
            $user->profile()->create([
                'bio' => fake()->paragraph(),
            ]);
        });
}

Generar modelos

make(): sin guardar en la BD

use App\Models\User;

// Genera 1 elemento
$user = User::factory()->make();

// Genera 3 (devuelve una colección)
$users = User::factory()->count(3)->make();

create(): guarda en la BD

// Genera y guarda 1
$user = User::factory()->create();

// Genera y guarda 3
$users = User::factory()->count(3)->create();

Sobrescribir atributos

Si pasas un array a make() o create(), se sobrescriben únicamente los atributos indicados.
$user = User::factory()->make([
    'name' => 'Ana García',
]);

$user = User::factory()->create([
    'name' => 'Luis Pérez',
    'email' => '[email protected]',
]);
También puedes usar state para sobrescribir de forma inline.
$user = User::factory()->state([
    'name' => 'María López',
])->make();
Al generar modelos con una factoría, la protección de asignación masiva se desactiva automáticamente.

Sequence

Cuando generas varios modelos y quieres alternar los valores, utiliza Sequence.
use App\Models\User;
use Illuminate\Database\Eloquent\Factories\Sequence;

$users = User::factory()
    ->count(10)
    ->state(new Sequence(
        ['admin' => 'Y'],
        ['admin' => 'N'],
    ))
    ->create();
// Se generarán 5 con admin='Y' y 5 con admin='N'
El método sequence() es una forma más concisa.
$users = User::factory()
    ->count(2)
    ->sequence(
        ['name' => 'Primer usuario'],
        ['name' => 'Segundo usuario'],
    )
    ->create();
También puedes generar valores dinámicos con una closure.
use Illuminate\Database\Eloquent\Factories\Sequence;

$users = User::factory()
    ->count(10)
    ->state(new Sequence(
        fn (Sequence $sequence) => ['name' => 'Usuario ' . $sequence->index],
    ))
    ->create();

Factorías de relaciones

Has Many (uno a muchos)

El método has() crea modelos con una relación «uno a muchos».
use App\Models\Post;
use App\Models\User;

// Un User con 3 Posts
$user = User::factory()
    ->has(Post::factory()->count(3))
    ->create();
Con métodos mágicos (has + nombre en plural de la relación) queda más breve.
$user = User::factory()
    ->hasPosts(3)
    ->create();

// Sobrescribiendo atributos
$user = User::factory()
    ->hasPosts(3, ['published' => false])
    ->create();

Belongs To (relación inversa)

Con for() indicas el modelo padre al que «pertenece».
use App\Models\Post;
use App\Models\User;

// 3 Posts pertenecientes a un usuario concreto
$posts = Post::factory()
    ->count(3)
    ->for(User::factory()->state(['name' => 'Ana García']))
    ->create();

// Usar un modelo existente como padre
$user = User::factory()->create();
$posts = Post::factory()->count(3)->for($user)->create();
Versión con métodos mágicos:
$posts = Post::factory()
    ->count(3)
    ->forUser(['name' => 'Ana García'])
    ->create();

Many to Many (muchos a muchos)

Con hasAttached() gestionas las relaciones muchos a muchos. También puedes indicar atributos para la tabla pivote.
use App\Models\Role;
use App\Models\User;

$user = User::factory()
    ->hasAttached(
        Role::factory()->count(3),
        ['active' => true]  // Atributos de la tabla pivote
    )
    ->create();
Versión con métodos mágicos:
$user = User::factory()
    ->hasRoles(1, ['name' => 'Editor'])
    ->create();

Relaciones polimórficas

Puedes usar has() o los métodos mágicos igual que en un «uno a muchos» normal.
use App\Models\Post;

$post = Post::factory()->hasComments(3)->create();
Las relaciones morphTo no admiten métodos mágicos. Debes indicar el nombre de la relación en for().
$comments = Comment::factory()->count(3)->for(
    Post::factory(), 'commentable'
)->create();

Definir relaciones dentro de la factoría

Si asignas otra factoría a una clave foránea en definition(), el modelo padre se genera automáticamente cuando se crea el hijo.
class PostFactory extends Factory
{
    public function definition(): array
    {
        return [
            'user_id' => \App\Models\User::factory(),
            'title' => fake()->sentence(),
            'content' => fake()->paragraph(),
        ];
    }
}

recycle(): reutilizar un modelo existente

Cuando quieras usar la misma instancia de un modelo en varias relaciones, utiliza recycle().
// Reutilizar el mismo Airline para Ticket y Flight
Ticket::factory()
    ->recycle(Airline::factory()->create())
    ->create();

// Elegir aleatoriamente de una colección
$airlines = Airline::factory()->count(3)->create();
Ticket::factory()->count(10)->recycle($airlines)->create();

Uso en seeders

Puedes invocar las factorías desde DatabaseSeeder o desde otros seeders.
namespace Database\Seeders;

use App\Models\User;
use Illuminate\Database\Seeder;

class DatabaseSeeder extends Seeder
{
    public function run(): void
    {
        // Crea 10 usuarios, cada uno con 3 posts
        User::factory()
            ->count(10)
            ->hasPosts(3)
            ->create();
    }
}
Ejecutar los seeders:
php artisan db:seed

Uso en tests

En los tests las factorías se combinan con el trait 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' => 'Prueba']);

        $response->assertForbidden();
    }
}
RefreshDatabase reinicia la base de datos en cada prueba, evitando interferencias entre ellas.
La API de las factorías es igual cuando pruebas con Pest. Utiliza uses(RefreshDatabase::class) para reiniciar la base de datos.

Próximos pasos

Seeding de la base de datos

Aprende a combinar seeders y factorías para cargar datos iniciales.

Introducción a los tests

Descubre las herramientas de testing de Laravel y cómo usar el trait RefreshDatabase.
Última modificación el 13 de julio de 2026