Laravel seeders and factories work as a pair. A factory describes what one fake record looks like, using the Faker library to fill in names, emails, dates, and other values. A seeder decides how many records to create and in what order. Together, one command, php artisan migrate:fresh --seed, can rebuild your database with realistic test data in seconds.
If you've ever demoed an app with three users called "test", "test2", and "asdf", you already know why this matters. Empty or fake-looking data can hide bugs, break layouts in front of clients, and make tests fragile.
This guide walks through Laravel factories and seeders from scratch: generating factories, using Faker effectively, states, relationships, organizing seeders, using factories in tests, and seeding large datasets without waiting all afternoon.
Key Takeaways
Factories define one record; seeders decide how many. Generate both with
php artisan make:model Post -mfs.Use
fake()insidedefinition()to generate realistic data, and setfaker_localeinconfig/app.phpfor local names and addresses.States keep factories reusable.
User::factory()->suspended()->create()is better than copy-pasting attribute arrays.Relationships are one line:
User::factory()->hasPosts(3)->create()orPost::factory()->for($user)->create().For 100K+ rows, use
raw()with chunked inserts instead ofcreate(), which saves one model at a time.
The developers in this article's examples are illustrative composites based on common Laravel scenarios.
What Are Laravel Seeders and Factories?
A factory is a class in database/factories that returns the default attributes for a model. Every new Laravel app ships with UserFactory. A seeder is a class in database/seeders with a run() method that inserts data. DatabaseSeeder is the entry point that runs when you call php artisan db:seed.
You'll use them in three places:
Local development, so every developer gets the same realistic dataset
Automated tests, so each test builds exactly the records it needs
Demos and staging, so clients see a believable app instead of placeholder text
The fastest way to create a model with its migration, factory, and seeder is with one command:
# -m = migration, -f = factory, -s = seeder
php artisan make:model Post -mfs
Laravel Factory Tutorial: Defining Your First Factory
Here's a factory for a Post model. Each key matches a column, and fake() generates a value every time the factory runs:
namespace Database\Factories;
use App\Models\User;
use Illuminate\Database\Eloquent\Factories\Factory;
use Illuminate\Support\Str;
/**
* @extends \Illuminate\Database\Eloquent\Factories\Factory<\App\Models\Post>
*/
class PostFactory extends Factory
{
public function definition(): array
{
$title = fake()->sentence(6);
return [
// Creates a new user unless you pass one in
'user_id' => User::factory(),
'title' => $title,
'slug' => Str::slug($title).'-'.fake()->unique()->numberBetween(1, 99999),
'body' => fake()->paragraphs(4, true),
'status' => fake()->randomElement(['draft', 'published', 'archived']),
'published_at' => fake()->optional(0.7)->dateTimeBetween('-1 year'),
];
}
}
Your model needs the HasFactory trait, which new models include by default. Then you can build records anywhere:
$post = Post::factory()->make(); // In memory, not saved
$post = Post::factory()->create(); // Saved to the database
$posts = Post::factory()->count(10)->create();
// Override any attribute
$post = Post::factory()->create(['title' => 'Launch day']);
Mass assignment protection is switched off when factories create models, so you don't need to add factory-only columns to $fillable.
Laravel Faker: Methods Worth Knowing
The fake() helper gives you the FakerPHP library. These methods cover most real apps:
Need | Faker method |
|---|---|
Person name, company |
|
Safe email (example.com domains) |
|
Phone, address |
|
Text |
|
Numbers and money |
|
Dates |
|
Pick from a list |
|
Sometimes null |
|
Two habits make test data feel real. First, set faker_locale in config/app.php (for example, en_GB or de_DE) so names, addresses, and phone numbers match your market. Second, use unique() on any column with a unique index, or your seeder will eventually fail with a duplicate-key error.
Prefer safeEmail() over email(). It uses reserved example domains, so a misconfigured mailer can never email a real person.
Factory States: One Factory, Many Scenarios
Real apps have users who are admins, suspended, or unverified. Don't build a factory for each. Add states:
// In UserFactory
public function admin(): static
{
return $this->state(fn (array $attributes) => [
'role' => 'admin',
]);
}
public function suspended(): static
{
return $this->state(fn (array $attributes) => [
'account_status' => 'suspended',
'suspended_at' => now(),
]);
}
Chain them as needed:
$admin = User::factory()->admin()->create();
$users = User::factory()->count(5)->suspended()->make();
If a model uses soft deletes, the built-in trashed() state is available without defining it.
Sequences for Variety
A sequence cycles through values as records are created. This creates 10 users, alternating between plans:
use Illuminate\Database\Eloquent\Factories\Sequence;
User::factory()
->count(10)
->state(new Sequence(
['plan' => 'free'],
['plan' => 'pro'],
))
->create();
Priya was testing a billing dashboard that showed revenue by plan. Her old seeder gave every user the same plan, so the chart showed one solid bar and a pricing bug stayed hidden for weeks. A three-value sequence (free, pro, team) surfaced the bug on the first run: team-plan revenue was being counted twice.
Callbacks for Side Effects
Use afterCreating when a record needs follow-up work, such as a default profile:
public function configure(): static
{
return $this->afterCreating(function (User $user) {
$user->profile()->create(['timezone' => 'UTC']);
});
}
Skip the boilerplate entirely? StackMivo generates Laravel apps with models, migrations, factories, and seeders already wired together, from a plain-English description.
Factory Relationships
Factories really pay off with relationships. Each pattern below is one line. If you need a refresher on defining the relationships themselves, see our guide to Eloquent relationships.
Has Many
// A user with three posts
$user = User::factory()->has(Post::factory()->count(3))->create();
// Same thing, using the magic method
$user = User::factory()->hasPosts(3)->create();
Belongs To
// Three posts by an existing user
$posts = Post::factory()->count(3)->for($user)->create();
Many-to-Many, with Pivot Data
$user = User::factory()
->hasAttached(Role::factory()->count(2), ['assigned_at' => now()])
->create();
Recycle Shared Parents
When several related models should share one parent, use recycle(). Without it, each nested factory creates its own parent, and you can end up with 50 teams when you wanted one:
$team = Team::factory()->create();
// Every project and every task belongs to the same team
Project::factory()
->count(5)
->has(Task::factory()->count(10))
->recycle($team)
->create();
Marcus noticed his local database had 1,200 companies after seeding what should have been 20. Every Invoice factory had 'company_id' => Company::factory(), and every Contact did the same. Passing his 20 companies to recycle() brought the count back to 20 and made the seeded invoices actually line up with their contacts.
Laravel Database Seeder: Organizing Your Seeders
Keep DatabaseSeeder short. Use it to call focused seeders in the right order, parents before children:
namespace Database\Seeders;
use Illuminate\Database\Console\Seeds\WithoutModelEvents;
use Illuminate\Database\Seeder;
class DatabaseSeeder extends Seeder
{
// Stops observers and model events (like welcome emails) firing during seeding
use WithoutModelEvents;
public function run(): void
{
$this->call([
RoleSeeder::class, // Reference data first
UserSeeder::class,
ProjectSeeder::class,
]);
}
}
Separate Reference Data from Demo Data
Not all seed data is fake. Roles, countries, and plan definitions are reference data your app needs in every environment. Make those seeders safe to run repeatedly with updateOrCreate:
class RoleSeeder extends Seeder
{
public function run(): void
{
foreach (['admin', 'editor', 'viewer'] as $name) {
// Safe to re-run: updates instead of duplicating
Role::updateOrCreate(['name' => $name]);
}
}
}
Keep demo data (fake users, posts, orders) in seeders that only run locally:
public function run(): void
{
$this->call(RoleSeeder::class);
if (app()->environment('local', 'testing')) {
$this->call(DemoDataSeeder::class);
}
}
Running Seeders
php artisan db:seed # Runs DatabaseSeeder
php artisan db:seed --class=RoleSeeder # Runs one seeder
php artisan migrate:fresh --seed # Drops all tables, migrates, seeds
Warning: migrate:fresh drops every table. Laravel asks for confirmation in the production environment, and --force skips that prompt. Never add --force to a production deploy script unless the seeder only touches idempotent reference data, and take a database backup before running any seeder against real data.
Using Factories in Tests
Factories make tests readable because each test creates exactly what it needs. With Pest:
use App\Models\Post;
use App\Models\User;
use Illuminate\Foundation\Testing\RefreshDatabase;
uses(RefreshDatabase::class);
it('only shows published posts on the blog', function () {
Post::factory()->count(3)->create(['status' => 'published']);
Post::factory()->count(2)->create(['status' => 'draft']);
$this->get('/blog')
->assertOk()
->assertViewHas('posts', fn ($posts) => $posts->count() === 3);
});
it('blocks suspended users from the dashboard', function () {
$user = User::factory()->suspended()->create();
$this->actingAs($user)->get('/dashboard')->assertForbidden();
});
RefreshDatabase resets the database between tests, so records from one test never leak into another. If a test needs your reference data, call $this->seed(RoleSeeder::class); at the start.
A good rule: create data in the test, not in a shared seeder. When a test fails, you can see every record it depends on without opening another file. Our Laravel testing guide covers this pattern in more depth, and AI test generation shows how to speed it up.
Seeding Large Datasets Fast
create() saves one model at a time and fires model events for each. That's fine for 500 records. For 100,000, it can take minutes.
Two fixes help most:
1. Reuse an expensive hashed value. The default UserFactory already does this with static::$password ??= Hash::make('password'), so it hashes once instead of once per user. Keep that line if you customize the factory.
2. Generate raw arrays and insert in chunks. raw() returns attribute arrays without saving anything. Then insert them in batches:
use App\Models\Product;
use Illuminate\Support\Facades\DB;
public function run(): void
{
// Build 100,000 attribute arrays, then insert 1,000 rows per query
collect(range(1, 100))->each(function () {
DB::table('products')->insert(
Product::factory()->count(1000)->raw()
);
});
}
This bypasses Eloquent events, casts, and automatic timestamps, so add created_at and updated_at to the factory if you need them, and use it only for simple tables. Avoid it when the factory creates parent models (like 'user_id' => User::factory()). Look up existing IDs and pass them in instead.
Elena needed 250,000 orders to load-test a reporting page. Her create() seeder ran for 22 minutes. Switching to raw() with 1,000-row inserts, and pulling customer IDs from a pre-seeded list, brought it to under two minutes. That made it practical to re-seed before every performance test.
Common Factory and Seeder Mistakes
Most seeding problems come from a handful of habits. Here's what to watch for.
Hard-coding IDs. Writing
'user_id' => 1works until someone seeds in a different order. UseUser::factory()or pass a real model withfor().Forgetting
unique(). Faker repeats itself sooner than you'd think. If a column has a unique index, wrap the Faker call inunique(), or your seeder will fail somewhere around record 5,000.Putting business logic in seeders. If your app sends a welcome email or creates a subscription when a user signs up, don't copy that into a seeder. Let the factory build the user, and use
afterCreatingfor anything the data genuinely needs.Giant shared seeders for tests. A test that relies on "the 50 users from
DemoSeeder" breaks when someone edits that seeder. Create test data inside the test.Data that's too clean. Real users leave fields blank, write very long names, and sign up on odd dates. Use
optional(), longer text, and wide date ranges, so your layouts and validation see what production will see.Not resetting between runs. Running
db:seedtwice duplicates demo data. Usemigrate:fresh --seedlocally, andupdateOrCreatefor anything that might run twice.
Fixing even two of these usually makes test failures easier to read and demos far more convincing.
Frequently Asked Questions
What is the difference between a seeder and a factory in Laravel?
A factory defines what one fake model looks like. A seeder runs code that inserts data, usually by calling factories to create many records. Factories are reused in tests; seeders set up whole databases.
How do I run a single seeder in Laravel?
Run php artisan db:seed --class=UserSeeder. To rebuild everything from scratch, run php artisan migrate:fresh --seed.
How do I change the Faker language in Laravel?
Set faker_locale in config/app.php, for example 'faker_locale' => 'fr_FR'. Names, addresses, and phone numbers will then match that locale.
Should I run seeders in production?
Only for reference data like roles or plans, and only with idempotent methods like updateOrCreate. Never run demo-data seeders against production, and back up the database first.
How do I create related models with Laravel factories?
Use has() or magic methods like hasPosts(3) for one-to-many, for($user) for belongs-to, and hasAttached() for many-to-many with pivot data.
Conclusion
Laravel seeders and factories turn test data from a chore into a single command. Define each model's shape once in a factory, add states for the scenarios that matter, wire up relationships with has(), for(), and recycle(), and let a short DatabaseSeeder call everything in order.
Start with one model today. Add a factory with realistic Faker data, a state or two, and a seeder you can run with migrate:fresh --seed. Your demos, tests, and teammates will all benefit.
Want factories and seeders generated alongside the rest of your app? Try StackMivo free with 20 credits →