Eloquent Factories and Seeders
Eloquent Factories: Tips & Tricks
Eloquent Factories: Tips & Tricks
Tip: Use States for Variants
public function published(): static
{
return $this->state(fn() => [
'published' => true,
'published_at' => now(),
]);
}
// Usage:
Post::factory()->published()->create();
Gotcha: Factory Relationships Need for()
Post::factory()->for(Category::factory())->create();
Creates both the category and the post.
Tip: Use has() for Related Models
Post::factory()->hasComments(5)->create();
Creates a post with 5 comments.
Gotcha: count() on create() vs make()
Post::factory()->count(10)->create(); // Saves to DB
Post::factory()->count(10)->make(); // Returns unsaved models
Tip: Use sequence() for Incrementing Values
Post::factory()
->count(10)
->state(new Sequence(
['position' => 1],
['position' => 2],
['position' => 3],
))
->create();
Tip: Faker Methods You Might Not Know
fake()->word(); // Single word
fake()->sentence(3); // 3-word sentence
fake()->imageUrl(); // Placeholder image URL
fake()->boolean(75); // 75% chance of true
Gotcha: Factory Callbacks
Post::factory()
->afterCreating(fn($post) => $post->tags()->attach(Tag::inRandomOrder()->limit(3)->get()))
->create();
Tip: Reset Factory State
Post::factory()->state([])->create();
Clears any previously set states.
Tip: Use cursor() for Memory-Neutral Iteration
When exporting 100K rows, get() loads everything into memory. cursor() uses yield and keeps memory flat regardless of row count. Perfect for artisan commands.
Tip: whereHas() vs load() — Two Different Things
whereHas() filters the parent query by relationship existence. load() eager-loads relationships AFTER the query. Mixing them up is a common source of logic bugs.
Gotcha: withCount() Adds a Subquery
withCount('comments') runs a correlated subquery on every row. On large tables, this can be slower than a separate query. Profile before relying on it.
Senior Insight
Factories are a testing cornerstone, but they're frequently misused. The number one issue: factories that create deeply nested related records by default, making tests slow and unpredictable. I design factories to create the minimum valid record — no relations unless explicitly requested via states. A test should set up exactly what it needs, nothing more. This keeps test suites fast and makes test setup readable.
Source: Laravel Docs (https://laravel.com/docs/eloquent), Laravel News (https://laravel-news.com/), Freek.dev (https://freek.dev/tags/eloquent)