Zum Hauptinhalt springen

Was ist eine Factory?

Bei Tests und beim Seeding müssen Datensätze in die Datenbank eingefügt werden. Statt für jede Spalte einen Wert von Hand anzugeben, definieren Sie mit einer Model-Factory in Laravel einen Satz an Standardattributen pro Eloquent-Modell. Jede neue Laravel-Anwendung enthält die Datei 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
{
    // Das gleiche Passwort nicht jedes Mal neu hashen
    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),
        ];
    }
}
Über den Helper fake() steht die Bibliothek Faker zur Verfügung, mit der Sie verschiedenste Zufallsdaten erzeugen können.
Die Faker-Locale können Sie in config/app.php über die Option faker_locale ändern.

Factory erzeugen

Erzeugen Sie eine Factory über den Artisan-Befehl make:factory.
php artisan make:factory PostFactory
Neue Factory-Klassen werden im Verzeichnis database/factories abgelegt.

Automatische Erkennung von Modell und Factory

Wenn ein Modell das Trait HasFactory verwendet, findet Laravel die zugehörige Factory automatisch. Gesucht wird im Namespace Database\Factories nach einer Klasse mit dem Namen des Modells plus Factory. Wenn die Namenskonvention nicht passt, können Sie die Zuordnung mit dem Attribut UseFactory explizit angeben.
use Illuminate\Database\Eloquent\Attributes\UseFactory;
use Database\Factories\Administration\FlightFactory;

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

Factory definieren

Methode definition()

Die zentrale Methode definition() liefert die Standardattribute.
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'),
        ];
    }
}
Häufig genutzte Faker-Methoden im Überblick:
MethodeBeispielausgabe
fake()->name()John Doe
fake()->email()[email protected]
fake()->sentence()Zufälliger Satz
fake()->paragraph()Zufälliger Absatz
fake()->numberBetween(1, 100)Ganzzahl zwischen 1 und 100
fake()->dateTime()Zufälliges Datum mit Uhrzeit
fake()->boolean()true / false

Factory States

Mit State-Methoden definieren Sie einzelne Varianten einer Factory.
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 für gesperrte Nutzer
    public function suspended(): static
    {
        return $this->state(fn (array $attributes) => [
            'account_status' => 'suspended',
        ]);
    }

    // State für Admin-Nutzer
    public function admin(): static
    {
        return $this->state(fn (array $attributes) => [
            'is_admin' => true,
        ]);
    }
}
States lassen sich kombinieren.
$user = User::factory()->suspended()->create();
$admin = User::factory()->admin()->create();

State für Soft Deletes

Für Modelle mit Soft Deletes steht der eingebaute State trashed() bereit.
$user = User::factory()->trashed()->create();

Factory-Callbacks

Mit afterMaking und afterCreating fügen Sie nach dem Erzeugen zusätzliche Verarbeitungsschritte hinzu.
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) {
            // Nach make() ausgeführt (nicht in der DB gespeichert)
        })->afterCreating(function (User $user) {
            // Nach create() ausgeführt (bereits in der DB)
        });
    }

    // ...
}
Auch innerhalb einer State-Methode können Callbacks registriert werden.
public function withProfile(): static
{
    return $this->state(fn (array $attributes) => [])
        ->afterCreating(function (User $user) {
            $user->profile()->create([
                'bio' => fake()->paragraph(),
            ]);
        });
}

Modelle erzeugen

make() – nicht in der DB speichern

use App\Models\User;

// Ein Modell erzeugen
$user = User::factory()->make();

// Drei Modelle erzeugen (Rückgabe als Collection)
$users = User::factory()->count(3)->make();

create() – in der DB speichern

// Ein Modell erzeugen und speichern
$user = User::factory()->create();

// Drei Modelle erzeugen und speichern
$users = User::factory()->count(3)->create();

Attribute überschreiben

Übergeben Sie an make() oder create() ein Array, um einzelne Attribute zu überschreiben.
$user = User::factory()->make([
    'name' => 'Yamada Taro',
]);

$user = User::factory()->create([
    'name' => 'Suzuki Hanako',
    'email' => '[email protected]',
]);
Auch das Überschreiben inline über einen State ist möglich.
$user = User::factory()->state([
    'name' => 'Tanaka Ichiro',
])->make();
Beim Erzeugen von Modellen über eine Factory wird der Schutz vor Mass Assignment automatisch deaktiviert.

Sequence

Wenn Sie beim Erzeugen mehrerer Modelle Attribute abwechselnd variieren möchten, verwenden Sie Sequence.
use App\Models\User;
use Illuminate\Database\Eloquent\Factories\Sequence;

$users = User::factory()
    ->count(10)
    ->state(new Sequence(
        ['admin' => 'Y'],
        ['admin' => 'N'],
    ))
    ->create();
// Fünfmal admin='Y', fünfmal admin='N'
Die Methode sequence() bietet eine kürzere Schreibweise.
$users = User::factory()
    ->count(2)
    ->sequence(
        ['name' => 'Erster Nutzer'],
        ['name' => 'Zweiter Nutzer'],
    )
    ->create();
Über eine Closure lassen sich Werte auch dynamisch erzeugen.
use Illuminate\Database\Eloquent\Factories\Sequence;

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

Factories für Relationen

Has Many (1:n)

Mit has() erzeugen Sie ein Modell mit einer 1:n-Relation.
use App\Models\Post;
use App\Models\User;

// Nutzer mit drei Beiträgen erzeugen
$user = User::factory()
    ->has(Post::factory()->count(3))
    ->create();
Mit Magic-Methoden geht es kürzer (has + Plural-Form des Beziehungsnamens).
$user = User::factory()
    ->hasPosts(3)
    ->create();

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

Belongs To (umgekehrte Relation)

Mit for() geben Sie das zugehörige Parent-Modell an.
use App\Models\Post;
use App\Models\User;

// Drei Beiträge für einen bestimmten Nutzer
$posts = Post::factory()
    ->count(3)
    ->for(User::factory()->state(['name' => 'Tanaka Hanako']))
    ->create();

// Ein vorhandenes Modell als Parent verwenden
$user = User::factory()->create();
$posts = Post::factory()->count(3)->for($user)->create();
Magic-Methoden-Variante:
$posts = Post::factory()
    ->count(3)
    ->forUser(['name' => 'Tanaka Hanako'])
    ->create();

Many to Many (n:m)

Für n:m-Beziehungen verwenden Sie hasAttached(). Auch Attribute der Zwischentabelle können angegeben werden.
use App\Models\Role;
use App\Models\User;

$user = User::factory()
    ->hasAttached(
        Role::factory()->count(3),
        ['active' => true]  // Attribute der Zwischentabelle
    )
    ->create();
Magic-Methoden-Variante:
$user = User::factory()
    ->hasRoles(1, ['name' => 'Editor'])
    ->create();

Polymorphe Relationen

Wie bei einer normalen 1:n-Relation können Sie has() oder die Magic-Methoden verwenden.
use App\Models\Post;

$post = Post::factory()->hasComments(3)->create();
Für morphTo-Relationen stehen keine Magic-Methoden zur Verfügung. Geben Sie den Beziehungsnamen explizit an for() weiter.
$comments = Comment::factory()->count(3)->for(
    Post::factory(), 'commentable'
)->create();

Relationen in der Factory

Weisen Sie in definition() einem Fremdschlüssel eine andere Factory zu, wird beim Erzeugen des Modells automatisch auch das Parent-Modell angelegt.
class PostFactory extends Factory
{
    public function definition(): array
    {
        return [
            'user_id' => \App\Models\User::factory(),
            'title' => fake()->sentence(),
            'content' => fake()->paragraph(),
        ];
    }
}

recycle() – bestehende Modelle wiederverwenden

Wenn Sie in mehreren Relationen dieselbe Modellinstanz wiederverwenden möchten, nutzen Sie recycle().
// Dieselbe Airline bei Ticket und Flight verwenden
Ticket::factory()
    ->recycle(Airline::factory()->create())
    ->create();

// Zufällig aus einer Collection wählen
$airlines = Airline::factory()->count(3)->create();
Ticket::factory()->count(10)->recycle($airlines)->create();

Verwendung beim Seeding

Rufen Sie Factories im DatabaseSeeder oder aus einer Seeder-Klasse heraus auf.
namespace Database\Seeders;

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

class DatabaseSeeder extends Seeder
{
    public function run(): void
    {
        // 10 Nutzer, jeder mit 3 Beiträgen
        User::factory()
            ->count(10)
            ->hasPosts(3)
            ->create();
    }
}
Seeder ausführen:
php artisan db:seed

Verwendung in Tests

In Tests kombinieren Sie Factories mit dem 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' => 'Test']);

        $response->assertForbidden();
    }
}
RefreshDatabase setzt die Datenbank zwischen den Tests zurück, damit sich die Daten der einzelnen Tests nicht gegenseitig beeinflussen.
Bei Tests mit Pest verwenden Sie dieselbe Factory-API. Setzen Sie die Datenbank mit uses(RefreshDatabase::class) zurück.

Nächste Schritte

Datenbank-Seeding

Erfahren Sie, wie Sie Seeder und Factories kombinieren, um initiale Daten einzuspielen.

Einführung in Tests

Vertiefen Sie die Test-Funktionen von Laravel und die Nutzung des RefreshDatabase-Traits.
Zuletzt geändert am 13. Juli 2026