0

Model factories — definition(), states, sequences

Intermediate5 min read·lv-15-002

Concept

Model factories are classes that know how to generate realistic fake instances of your Eloquent models. Every factory extends Illuminate\Database\Eloquent\Factories\Factory and implements a single required method, definition(), which returns an array of default attribute values. Laravel wires a factory to its model via the HasFactory trait, which adds the static factory() method to every model and uses a naming convention — App\Models\Post maps to Database\Factories\PostFactory — to resolve the right class without any registration.

The definition() method receives a $this->faker instance (a Faker\Generator) that you use to generate realistic data. Each call to factory() creates a new Faker\Generator seeded from config('app.faker_seed'), so results are random by default but reproducible in tests when you fix the seed. The factory itself is a value object: calling factory() returns a Factory instance and nothing is written to the database until you call create() or createMany().

States are named variations of a factory's definition(). You define them by adding methods that return $this->state(fn (array $attributes) => [...]). States compose cleanly — User::factory()->admin()->suspended()->create() merges the state arrays in order, with later states winning on key conflicts. This composability lets you describe complex scenarios (->banned()->withExpiredSubscription()) without building separate factory classes.

Sequences let you cycle through a set of values across multiple model instances. $this->sequence(['role' => 'admin'], ['role' => 'editor'], ['role' => 'viewer']) assigns roles in rotation when you call factory()->count(9)->create(). You can also pass a closure to sequence() for computed values that depend on the current index.

Factory methodReturnsPersists to DB
make()Model instanceNo
create()Model instanceYes
makeMany(n)CollectionNo
createMany(n)CollectionYes
raw()Plain arrayNo

Code Example

php
<?php

namespace Database\Factories;

use App\Models\User;
use Illuminate\Database\Eloquent\Factories\Factory;
use Illuminate\Support\Str;

/**
 * @extends Factory<User>
 */
class UserFactory extends Factory
{
    // Override if the naming convention doesn't apply
    protected $model = User::class;

    public function definition(): array
    {
        return [
            'name'              => fake()->name(),
            'email'             => fake()->unique()->safeEmail(),
            'email_verified_at' => now(),
            'password'          => bcrypt('password'), // fixed for dev convenience
            'remember_token'    => Str::random(10),
        ];
    }

    // --- States ---

    /** Admin user with elevated role flag */
    public function admin(): static
    {
        return $this->state(fn (array $attributes) => [
            'role' => 'admin',
        ]);
    }

    /** Unverified user — email_verified_at is null */
    public function unverified(): static
    {
        return $this->state(fn (array $attributes) => [
            'email_verified_at' => null,
        ]);
    }

    /** Suspended user */
    public function suspended(): static
    {
        return $this->state(fn (array $attributes) => [
            'suspended_at' => now()->subDays(7),
        ]);
    }

    // --- Sequence example ---

    /** Rotate through three roles for a batch */
    public function withRotatingRoles(): static
    {
        return $this->sequence(
            ['role' => 'admin'],
            ['role' => 'editor'],
            ['role' => 'viewer'],
        );
    }
}

// Usage in a test or seeder:
// User::factory()->count(3)->withRotatingRoles()->create();
// User::factory()->admin()->unverified()->make(); // no DB write

Interview Q&A

Q: How does the HasFactory trait locate the correct factory class for a model?

HasFactory::factory() calls the static newFactory() method, which by default uses Factory::resolveFactoryName(static::class). That resolver strips the App\Models\ namespace prefix, prepends Database\Factories\, and appends Factory — so App\Models\BlogPost resolves to Database\Factories\BlogPostFactory. You can override newFactory() on the model to return a specific factory instance when your project's namespace or directory structure doesn't match the convention. This design means zero registration overhead for the common case.


Q: What is the difference between a factory state and overriding attributes directly in create()?

Both merge attributes over the definition(), but states are reusable, named, and composable: you can chain multiple states and they read like a specification (->admin()->suspended()). Direct attribute overrides in create(['role' => 'admin']) are one-shot and not reusable across tests. Use states whenever the same combination of attributes appears in more than one test, and use inline overrides for truly one-off values like a specific email address for a known test case.


Q: How do sequences differ from states, and when would you prefer one over the other?

A state always produces the same fixed attribute values — it describes a stable variant. A sequence cycles through a list of attribute sets across multiple instances created in a single factory()->count(n) call, making it ideal for distributing varied but predictable data (e.g., alternating statuses, rotating roles, incrementing dates). Use a state when you need a named, documented variant of a model; use a sequence when you need controlled variety across a batch of records without writing a loop.