In one sentence

A factory is a recipe for building fake models in one line, and a seeder uses those recipes to fill your database with realistic data.

๐ŸชA factory is a cookie cutter: one shape, as many cookies as you like, and you can add sprinkles (states) when needed. A seeder is the baker who fills the display case each morning before the shop opens.

Why it matters

Data on demand

๐Ÿงช

Short, readable tests

Post::factory()->create() replaces ten lines of setup, so tests show only what matters.

๐Ÿ—๏ธ

Realistic local apps

Develop against 50 users and 300 posts instead of an empty screen.

๐Ÿ‘ฅ

Same data for the team

Anyone can rebuild an identical development database with one command.

The big picture

How the pieces fit

๐Ÿ“FactoryPostFactory::definition()
๐ŸŒฑSeederPostSeeder::run()
๐ŸŽ›๏ธDatabaseSeedercalls every seeder
๐Ÿ—„๏ธDatabasedb:seed
Factories describe one fake model. Seeders decide how many to create and in what order. Tests use factories directly.
database/
โ”œโ”€โ”€ factories/
โ”‚   โ”œโ”€โ”€ UserFactory.php      โ† ships with Laravel
โ”‚   โ””โ”€โ”€ PostFactory.php
โ”œโ”€โ”€ migrations/
โ””โ”€โ”€ seeders/
    โ”œโ”€โ”€ DatabaseSeeder.php   โ† entry point for db:seed
    โ””โ”€โ”€ PostSeeder.php
Step by step

Fake a whole blog

  1. Define a factory

    Generate a factory for the model. In definition(), return default values. fake() gives you Faker, a library of realistic random data.

    
              php artisan make:factory PostFactory --model=Post
            
    
              <?php
    
              namespace Database\Factories;
    
              use App\Models\User;
              use Illuminate\Database\Eloquent\Factories\Factory;
    
              class PostFactory extends Factory
              {
                  public function definition(): array
                  {
                      return [
                          'user_id'      => User::factory(),
                          'title'        => fake()->sentence(),
                          'body'         => fake()->paragraphs(4, true),
                          'published_at' => now(),
                      ];
                  }
              }
            

    The model needs the HasFactory trait (models made with make:model already have it). 'user_id' => User::factory() creates an author automatically when none is given.

  2. Use it: make, create and count

    make() builds models in memory only; create() also saves them. Override any field by passing an array.

    
              $post  = Post::factory()->make();                       // not saved
              $post  = Post::factory()->create();                     // saved + author created
              $posts = Post::factory()->count(10)->create();          // 10 posts
              $post  = Post::factory()->create(['title' => 'Pinned']); // override a field
            
  3. Add states for variations

    A state is a named tweak to the defaults, like a draft post. It keeps tests readable: Post::factory()->draft() says exactly what you mean.

    
              public function draft(): static
              {
                  return $this->state(fn (array $attributes) => [
                      'published_at' => null,
                  ]);
              }
            
    
              test('drafts are hidden from the blog index', function () {
                  Post::factory()->draft()->create(['title' => 'Secret draft']);
    
                  $this->get('/posts')->assertDontSee('Secret draft');
              });
            
  4. Build relationships with has and for

    Factories understand your Eloquent relationships. Use has() to create children and for() to attach a parent.

    
              // A user with 3 posts, each with 5 comments
              $user = User::factory()
                  ->has(Post::factory()->count(3)->has(Comment::factory()->count(5)))
                  ->create();
    
              // Magic shortcut for the same "has many" idea
              $user = User::factory()->hasPosts(3)->create();
    
              // A post that belongs to a specific user and category
              $post = Post::factory()
                  ->for($user)
                  ->for(Category::factory()->state(['name' => 'Laravel']))
                  ->create();
            
  5. Write seeders and wire up DatabaseSeeder

    A seeder is a class with a run() method that creates data. DatabaseSeeder is the entry point; it calls the others in order.

    
              php artisan make:seeder PostSeeder
            
    
              class DatabaseSeeder extends Seeder
              {
                  public function run(): void
                  {
                      User::factory()->create([
                          'name'  => 'Test User',
                          'email' => 'test@example.com',   // a known login for local dev
                      ]);
    
                      $this->call([CategorySeeder::class, PostSeeder::class]);
                  }
              }
            
    
              public function run(): void
              {
                  User::factory(10)
                      ->has(Post::factory()->count(5)->for(Category::inRandomOrder()->first()))
                      ->create();
              }
            
  6. Run the seeders

    db:seed runs DatabaseSeeder. migrate:fresh --seed drops every table, re-runs all migrations, then seeds: a perfect reset button for local development.

    
              php artisan db:seed                          # run DatabaseSeeder
              php artisan db:seed --class=PostSeeder       # run just one seeder
              php artisan migrate:fresh --seed             # wipe, rebuild, seed
            
    โš ๏ธ Never run migrate:fresh in production

    It deletes every table and all data. Laravel asks for confirmation in production, but treat it as a local-only command.

    In tests, call $this->seed() (or $this->seed(CategorySeeder::class)) when you need reference data like categories.

Common mistakes

Avoid this, do that

โŒ Avoid

          $user = User::create([
              'name' => 'A', 'email' => 'a@a.com',
              'password' => bcrypt('x'),
          ]);
          $post = Post::create([...]);
        

Hand-built setup is long, repetitive and breaks whenever a required column is added.

โœ… Do

          $post = Post::factory()
              ->for(User::factory())
              ->create();
        

Factories fill in sensible defaults, so your test only states what's special.

โŒ Avoid

          'published_at' => rand(0, 1) ? now() : null,
        

Random defaults make tests pass sometimes and fail other times ("flaky" tests).

โœ… Do

          'published_at' => now(),
          // and a draft() state for null
        

Keep defaults predictable and express variations as named states.

๐Ÿ’ก Pro tip: tinker with factories

Run php artisan tinker and type Post::factory()->count(3)->make() to preview what your factory produces without saving anything.

๐Ÿ‹๏ธ Try it yourself
  1. Create CategoryFactory and CommentFactory for the blog.
  2. Add a draft() state and a popular() state (e.g. views over 1000) to PostFactory.
  3. Seed 5 categories, 10 users and 50 posts, each with 0โ€“5 comments.
  4. Run php artisan migrate:fresh --seed and browse your filled-up blog.
  5. Rewrite one feature test from the previous lesson to use has() and for().
Recap

Key takeaways

Check yourself

Mini quiz