> ## Documentation Index
> Fetch the complete documentation index at: https://kawax.biz/llms.txt
> Use this file to discover all available pages before exploring further.

# Eloquent-factories

> Uitleg over hoe je met modelfactories efficiënt nepdata genereert voor tests en seeding.

## Wat zijn factories?

Bij tests en databaseseeding moet je records in de database invoegen.
In plaats van handmatig de waarde van elke kolom op te geven, kun je met de **modelfactories** van Laravel een set standaardattributen definiëren voor elk Eloquent-model.

Elke nieuwe Laravel-applicatie wordt geleverd met `database/factories/UserFactory.php`.

```php theme={null}
namespace Database\Factories;

use Illuminate\Database\Eloquent\Factories\Factory;
use Illuminate\Support\Facades\Hash;
use Illuminate\Support\Str;

class UserFactory extends Factory
{
    // Cache hetzelfde wachtwoord zodat het niet elke keer opnieuw wordt gehasht
    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),
        ];
    }
}
```

Via de `fake()`-helper heb je toegang tot de [Faker](https://github.com/FakerPHP/Faker)-library, waarmee je allerlei willekeurige data kunt genereren.

```mermaid theme={null}
flowchart LR
    F["Factory<br>database/factories/UserFactory"] -->|"make() / create()"| M["Model Instance<br>App\\Models\\User"]
    M -->|"create()"| DB[("Database")]
    F -->|"Faker"| R["Willekeurige data<br>name, email, ..."]
    R --> F
```

<Info>
  De locale van Faker kun je wijzigen met de optie `faker_locale` in `config/app.php`.
</Info>

## Factories genereren

Maak een factory aan met het Artisan-commando `make:factory`.

```shell theme={null}
php artisan make:factory PostFactory
```

De nieuwe factoryklasse wordt geplaatst in de map `database/factories`.

### Automatische detectie van modellen en factories

Als je model de `HasFactory`-trait gebruikt, vindt Laravel automatisch de bijbehorende factory.
Er wordt in de namespace `Database\Factories` gezocht naar een klassennaam die bestaat uit de modelnaam met `Factory` erachter.

Als de naamgevingsconventie niet past, kun je de factory expliciet opgeven met het `UseFactory`-attribuut.

```php theme={null}
use Illuminate\Database\Eloquent\Attributes\UseFactory;
use Database\Factories\Administration\FlightFactory;

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

## Factories definiëren

### De `definition()`-methode

In de centrale `definition()`-methode van de factory definieer je de standaardattributen.

```php theme={null}
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'),
        ];
    }
}
```

Een overzicht van veelgebruikte Faker-methoden:

| Methode                         | Voorbeeld van gegenereerde data |
| ------------------------------- | ------------------------------- |
| `fake()->name()`                | `John Doe`                      |
| `fake()->email()`               | `john@example.com`              |
| `fake()->sentence()`            | Willekeurige zin                |
| `fake()->paragraph()`           | Willekeurige alinea             |
| `fake()->numberBetween(1, 100)` | Geheel getal van `1` t/m `100`  |
| `fake()->dateTime()`            | Willekeurige datum en tijd      |
| `fake()->boolean()`             | `true` / `false`                |

## Factory-states

Met state-methoden kun je afzonderlijke wijzigingen op een factory definiëren.

```php theme={null}
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',
        ];
    }

    // State voor geschorste gebruikers
    public function suspended(): static
    {
        return $this->state(fn (array $attributes) => [
            'account_status' => 'suspended',
        ]);
    }

    // State voor beheerders
    public function admin(): static
    {
        return $this->state(fn (array $attributes) => [
            'is_admin' => true,
        ]);
    }
}
```

States kun je combineren.

```php theme={null}
$user = User::factory()->suspended()->create();
$admin = User::factory()->admin()->create();
```

### De soft-delete-state

Voor modellen die soft deletes ondersteunen, is de ingebouwde `trashed()`-state beschikbaar.

```php theme={null}
$user = User::factory()->trashed()->create();
```

## Factory-callbacks

Met `afterMaking` en `afterCreating` definieer je extra logica na het genereren van een model.

```php theme={null}
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) {
            // Uitgevoerd na make() (nog niet opgeslagen in de DB)
        })->afterCreating(function (User $user) {
            // Uitgevoerd na create() (al opgeslagen in de DB)
        });
    }

    // ...
}
```

Je kunt ook binnen state-methoden callbacks registreren.

```php theme={null}
public function withProfile(): static
{
    return $this->state(fn (array $attributes) => [])
        ->afterCreating(function (User $user) {
            $user->profile()->create([
                'bio' => fake()->paragraph(),
            ]);
        });
}
```

## Modellen genereren

### make() — niet opslaan in de DB

```php theme={null}
use App\Models\User;

// 1 exemplaar genereren
$user = User::factory()->make();

// 3 exemplaren genereren (teruggegeven als collectie)
$users = User::factory()->count(3)->make();
```

### create() — opslaan in de DB

```php theme={null}
// 1 exemplaar genereren en opslaan in de DB
$user = User::factory()->create();

// 3 exemplaren genereren en opslaan in de DB
$users = User::factory()->count(3)->create();
```

### Attributen overschrijven

Als je een array doorgeeft aan `make()` of `create()`, kun je alleen specifieke attributen overschrijven.

```php theme={null}
$user = User::factory()->make([
    'name' => 'Yamada Taro',
]);

$user = User::factory()->create([
    'name' => 'Suzuki Hanako',
    'email' => 'hanako@example.com',
]);
```

Inline overschrijven met een state is ook mogelijk.

```php theme={null}
$user = User::factory()->state([
    'name' => 'Tanaka Ichiro',
])->make();
```

<Info>
  Bij het genereren van modellen met een factory wordt de mass-assignment-bescherming automatisch uitgeschakeld.
</Info>

### Sequence

Wil je bij het genereren van meerdere modellen attributen afwisselen, gebruik dan `Sequence`.

```php theme={null}
use App\Models\User;
use Illuminate\Database\Eloquent\Factories\Sequence;

$users = User::factory()
    ->count(10)
    ->state(new Sequence(
        ['admin' => 'Y'],
        ['admin' => 'N'],
    ))
    ->create();
// 5 exemplaren worden gegenereerd met admin='Y', 5 met admin='N'
```

Met de `sequence()`-methode kun je dit beknopter schrijven.

```php theme={null}
$users = User::factory()
    ->count(2)
    ->sequence(
        ['name' => 'Eerste gebruiker'],
        ['name' => 'Tweede gebruiker'],
    )
    ->create();
```

Je kunt ook met een closure dynamisch willekeurige waarden genereren.

```php theme={null}
use Illuminate\Database\Eloquent\Factories\Sequence;

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

## Factories voor relaties

```mermaid theme={null}
flowchart TD
    UF["UserFactory"] -->|"has(PostFactory)"| PF["PostFactory"]
    PF -->|"for(UserFactory)"| UF
    UF -->|"hasAttached(RoleFactory)"| RF["RoleFactory"]

    UF -->|"create()"| U["User-model"]
    PF -->|"create()"| P["Post-model"]
    RF -->|"create()"| R["Role-model"]

    U -->|"hasMany"| P
    U -->|"belongsToMany"| R
```

### Has many (één-op-veel)

Met de `has()`-methode genereer je een model met een "één-op-veel"-relatie.

```php theme={null}
use App\Models\Post;
use App\Models\User;

// Genereer een User met 3 Posts
$user = User::factory()
    ->has(Post::factory()->count(3))
    ->create();
```

Met magic methods kun je dit beknopter schrijven (`has` + de meervoudsvorm van de relatienaam).

```php theme={null}
$user = User::factory()
    ->hasPosts(3)
    ->create();

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

### Belongs to (omgekeerde relatie)

Met de `for()`-methode geef je het bovenliggende model op waartoe het model "behoort".

```php theme={null}
use App\Models\Post;
use App\Models\User;

// Genereer 3 Posts die bij een specifieke gebruiker horen
$posts = Post::factory()
    ->count(3)
    ->for(User::factory()->state(['name' => 'Tanaka Hanako']))
    ->create();

// Een bestaand model als parent gebruiken
$user = User::factory()->create();
$posts = Post::factory()->count(3)->for($user)->create();
```

De variant met magic methods:

```php theme={null}
$posts = Post::factory()
    ->count(3)
    ->forUser(['name' => 'Tanaka Hanako'])
    ->create();
```

### Many to many (veel-op-veel)

Met de `hasAttached()`-methode werk je met veel-op-veel-relaties. Je kunt ook attributen voor de tussentabel opgeven.

```php theme={null}
use App\Models\Role;
use App\Models\User;

$user = User::factory()
    ->hasAttached(
        Role::factory()->count(3),
        ['active' => true]  // Attributen van de tussentabel
    )
    ->create();
```

De variant met magic methods:

```php theme={null}
$user = User::factory()
    ->hasRoles(1, ['name' => 'Editor'])
    ->create();
```

### Polymorfe relaties

Net als bij gewone "één-op-veel"-relaties kun je `has()` of magic methods gebruiken.

```php theme={null}
use App\Models\Post;

$post = Post::factory()->hasComments(3)->create();
```

Voor `morphTo`-relaties kun je geen magic methods gebruiken. Geef de relatienaam expliciet op aan `for()`.

```php theme={null}
$comments = Comment::factory()->count(3)->for(
    Post::factory(), 'commentable'
)->create();
```

### Relaties binnen de factory definiëren

Als je in `definition()` een andere factory toewijst aan een foreign key, wordt bij het genereren van het model automatisch ook het bovenliggende model gegenereerd.

```php theme={null}
class PostFactory extends Factory
{
    public function definition(): array
    {
        return [
            'user_id' => \App\Models\User::factory(),
            'title' => fake()->sentence(),
            'content' => fake()->paragraph(),
        ];
    }
}
```

### recycle() — bestaande modellen hergebruiken

Wil je dezelfde modelinstantie hergebruiken in meerdere relaties, gebruik dan `recycle()`.

```php theme={null}
// Gebruik dezelfde Airline voor zowel Ticket als Flight
Ticket::factory()
    ->recycle(Airline::factory()->create())
    ->create();

// Willekeurig kiezen uit een collectie
$airlines = Airline::factory()->count(3)->create();
Ticket::factory()->count(10)->recycle($airlines)->create();
```

## Gebruik bij seeding

Roep factories aan vanuit de `DatabaseSeeder` of een seederklasse.

```php theme={null}
namespace Database\Seeders;

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

class DatabaseSeeder extends Seeder
{
    public function run(): void
    {
        // Genereer 10 gebruikers en geef elke gebruiker 3 posts
        User::factory()
            ->count(10)
            ->hasPosts(3)
            ->create();
    }
}
```

De seeder uitvoeren:

```shell theme={null}
php artisan db:seed
```

## Gebruik in tests

In tests gebruik je factories in combinatie met de `RefreshDatabase`-trait.

```php theme={null}
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();
    }
}
```

Omdat `RefreshDatabase` de database bij elke test reset, interfereert data niet tussen tests.

<Tip>
  Ook bij het testen met Pest is de factory-API hetzelfde.
  Gebruik `uses(RefreshDatabase::class)` om de database te resetten.
</Tip>

## Volgende stappen

<Card title="Databaseseeding" icon="seedling" href="/nl/seeding">
  Bekijk hoe je seeders en factories combineert om initiële data in te laden.
</Card>

<Card title="Introductie tot testen" icon="flask" href="/nl/testing">
  Bekijk de testfunctionaliteit van Laravel en het gebruik van de RefreshDatabase-trait.
</Card>


## Related topics

- [Database seeding](/nl/seeding.md)
- [Databasetests](/nl/database-testing.md)
- [Eloquent bootable traits](/nl/advanced/eloquent-bootable-traits.md)
- [Contracts](/nl/contracts.md)
- [HTTP-tests](/nl/http-tests.md)
