Laravel Seeding and Factories: Populating Your Database with Fake Data for Testing

Published on November 16, 2025
Laravel Seeding Factories Testing Database Faker

Introduction to Database Seeding

Database seeding is the process of populating your database with sample or test data. This is essential for:

  • Development: Having realistic data to work with
  • Testing: Consistent data for automated tests
  • Demonstrations: Showing features with actual data
  • Onboarding: New team members can get started quickly

Laravel Seeder Basics

Creating Seeders

# Create a new seeder
php artisan make:seeder UserSeeder

# Create multiple seeders
php artisan make:seeder PostSeeder
php artisan make:seeder CommentSeeder

Basic Seeder Structure

<?php
// database/seeders/UserSeeder.php

namespace Database\Seeders;

use Illuminate\Database\Console\Seeds\WithoutModelEvents;
use Illuminate\Database\Seeder;
use App\Models\User;

class UserSeeder extends Seeder
{
    /**
     * Run the database seeds.
     */
    public function run(): void
    {
        // Basic user creation
        User::create([
            'name' => 'John Doe',
            'email' => 'john@teachyleaf.com',
            'password' => bcrypt('password123'),
        ]);

        // Multiple users
        User::create([
            'name' => 'Jane Smith',
            'email' => 'jane@teachyleaf.com',
            'password' => bcrypt('password123'),
        ]);

        User::create([
            'name' => 'Admin User',
            'email' => 'admin@teachyleaf.com',
            'password' => bcrypt('password123'),
            'is_admin' => true,
        ]);
    }
}

Running Seeders

# Run all seeders
php artisan db:seed

# Run specific seeder
php artisan db:seed --class=UserSeeder

# Refresh database and seed
php artisan migrate:refresh --seed

# Fresh migration and seed
php artisan migrate:fresh --seed

Model Factories: Generating Fake Data

Creating Model Factories

# Create factory for User model
php artisan make:factory UserFactory

# Create factory for specific model
php artisan make:factory PostFactory --model=Post

# Create multiple factories
php artisan make:factory CategoryFactory --model=Category
php artisan make:factory TagFactory --model=Tag

Basic Factory Structure

<?php
// database/factories/UserFactory.php

namespace Database\Factories;

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

/**
 * @extends \Illuminate\Database\Eloquent\Factories\Factory<\App\Models\User>
 */
class UserFactory extends Factory
{
    /**
     * The current password being used by the factory.
     */
    protected static ?string $password;

    /**
     * Define the model's default state.
     *
     * @return array<string, mixed>
     */
    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),
        ];
    }

    /**
     * Indicate that the model's email address should be unverified.
     */
    public function unverified(): static
    {
        return $this->state(fn (array $attributes) => [
            'email_verified_at' => null,
        ]);
    }
}

Faker Library: Generating Realistic Data

Common Faker Methods

// Names and Contact
'name' => fake()->name(),
'first_name' => fake()->firstName(),
'last_name' => fake()->lastName(),
'email' => fake()->unique()->safeEmail(),
'phone' => fake()->phoneNumber(),

// Address
'address' => fake()->address(),
'city' => fake()->city(),
'state' => fake()->state(),
'country' => fake()->country(),
'zip_code' => fake()->postcode(),

// Text Content
'title' => fake()->sentence(),
'sentence' => fake()->sentence(6),
'paragraph' => fake()->paragraph(3),
'text' => fake()->text(200),

// Numbers and Dates
'number' => fake()->randomNumber(2),
'price' => fake()->randomFloat(2, 10, 100),
'age' => fake()->numberBetween(18, 65),
'birth_date' => fake()->date(),

// Internet
'username' => fake()->userName(),
'domain' => fake()->domainName(),
'url' => fake()->url(),
'slug' => fake()->slug(),

// Business
'company' => fake()->company(),
'job_title' => fake()->jobTitle(),

// Images and Files
'image_url' => fake()->imageUrl(640, 480, 'nature', true),

// Boolean and Enums
'is_active' => fake()->boolean(),
'status' => fake()->randomElement(['active', 'inactive', 'pending']),

Complete Factory Examples

User Factory with Multiple States

<?php
// 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
{
    protected static ?string $password;

    public function definition(): array
    {
        return [
            'name' => fake()->name(),
            'email' => fake()->unique()->safeEmail(),
            'username' => fake()->unique()->userName(),
            'phone' => fake()->phoneNumber(),
            'bio' => fake()->paragraph(2),
            'avatar' => fake()->imageUrl(100, 100, 'people', true),
            'website' => fake()->url(),
            'location' => fake()->city() . ', ' . fake()->country(),
            'email_verified_at' => now(),
            'password' => static::$password ??= Hash::make('password'),
            'remember_token' => Str::random(10),
            'created_at' => fake()->dateTimeBetween('-1 year', 'now'),
            'updated_at' => fake()->dateTimeBetween('-1 year', 'now'),
        ];
    }

    public function unverified(): static
    {
        return $this->state(fn (array $attributes) => [
            'email_verified_at' => null,
        ]);
    }

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

    public function moderator(): static
    {
        return $this->state(fn (array $attributes) => [
            'role' => 'moderator',
        ]);
    }

    public function withSpecificName(string $name): static
    {
        return $this->state(fn (array $attributes) => [
            'name' => $name,
        ]);
    }

    public function inactive(): static
    {
        return $this->state(fn (array $attributes) => [
            'is_active' => false,
        ]);
    }

    public function suspended(): static
    {
        return $this->state(fn (array $attributes) => [
            'suspended_at' => now(),
            'suspension_reason' => fake()->sentence(),
        ]);
    }
}

Post Factory with Relationships

<?php
// database/factories/PostFactory.php

namespace Database\Factories;

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

class PostFactory extends Factory
{
    public function definition(): array
    {
        $title = fake()->sentence(6);
        
        return [
            'title' => $title,
            'slug' => Str::slug($title),
            'excerpt' => fake()->paragraph(2),
            'content' => $this->generateContent(),
            'featured_image' => fake()->imageUrl(800, 400, 'nature', true),
            'meta_title' => fake()->sentence(),
            'meta_description' => fake()->paragraph(),
            'is_published' => fake()->boolean(80), // 80% chance of being published
            'is_featured' => fake()->boolean(20), // 20% chance of being featured
            'published_at' => fake()->dateTimeBetween('-1 year', '+1 month'),
            'views' => fake()->numberBetween(0, 10000),
            'reading_time' => fake()->numberBetween(1, 20),
            'user_id' => \App\Models\User::factory(),
            'created_at' => fake()->dateTimeBetween('-1 year', 'now'),
            'updated_at' => fake()->dateTimeBetween('-1 year', 'now'),
        ];
    }

    private function generateContent(): string
    {
        $paragraphs = fake()->paragraphs(rand(5, 15));
        $content = '';
        
        foreach ($paragraphs as $paragraph) {
            $content .= "<p>{$paragraph}</p>\n\n";
            
            // Occasionally add headings
            if (rand(1, 5) === 1) {
                $content .= "<h2>" . fake()->sentence(4) . "</h2>\n\n";
            }
            
            // Occasionally add blockquotes
            if (rand(1, 8) === 1) {
                $content .= "<blockquote>" . fake()->sentence(10) . "</blockquote>\n\n";
            }
        }
        
        return $content;
    }

    public function published(): static
    {
        return $this->state(fn (array $attributes) => [
            'is_published' => true,
            'published_at' => fake()->dateTimeBetween('-1 year', 'now'),
        ]);
    }

    public function draft(): static
    {
        return $this->state(fn (array $attributes) => [
            'is_published' => false,
            'published_at' => null,
        ]);
    }

    public function featured(): static
    {
        return $this->state(fn (array $attributes) => [
            'is_featured' => true,
        ]);
    }

    public function withUser(int $userId): static
    {
        return $this->state(fn (array $attributes) => [
            'user_id' => $userId,
        ]);
    }

    public function withSpecificTitle(string $title): static
    {
        return $this->state(fn (array $attributes) => [
            'title' => $title,
            'slug' => Str::slug($title),
        ]);
    }
}

Comment Factory with Polymorphic Relationships

<?php
// database/factories/CommentFactory.php

namespace Database\Factories;

use Illuminate\Database\Eloquent\Factories\Factory;

class CommentFactory extends Factory
{
    public function definition(): array
    {
        // Randomly decide if this is for a Post or Video
        $commentableType = fake()->randomElement([
            \App\Models\Post::class,
            \App\Models\Video::class,
        ]);

        return [
            'body' => fake()->paragraph(rand(1, 4)),
            'user_id' => \App\Models\User::factory(),
            'commentable_type' => $commentableType,
            'commentable_id' => $commentableType::factory(),
            'is_approved' => fake()->boolean(85), // 85% approved
            'upvotes' => fake()->numberBetween(0, 100),
            'downvotes' => fake()->numberBetween(0, 50),
            'created_at' => fake()->dateTimeBetween('-1 year', 'now'),
            'updated_at' => fake()->dateTimeBetween('-1 year', 'now'),
        ];
    }

    public function approved(): static
    {
        return $this->state(fn (array $attributes) => [
            'is_approved' => true,
        ]);
    }

    public function pending(): static
    {
        return $this->state(fn (array $attributes) => [
            'is_approved' => false,
        ]);
    }

    public function forPost($post = null): static
    {
        $post = $post ?? \App\Models\Post::factory();

        return $this->state(fn (array $attributes) => [
            'commentable_type' => \App\Models\Post::class,
            'commentable_id' => $post,
        ]);
    }

    public function forVideo($video = null): static
    {
        $video = $video ?? \App\Models\Video::factory();

        return $this->state(fn (array $attributes) => [
            'commentable_type' => \App\Models\Video::class,
            'commentable_id' => $video,
        ]);
    }

    public function withUser($user): static
    {
        return $this->state(fn (array $attributes) => [
            'user_id' => $user,
        ]);
    }
}

Advanced Seeding Techniques

Database Seeder Class

<?php
// database/seeders/DatabaseSeeder.php

namespace Database\Seeders;

use Illuminate\Database\Seeder;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Hash;
use Illuminate\Support\Str;

class DatabaseSeeder extends Seeder
{
    public function run(): void
    {
        // Disable foreign key checks for better performance
        DB::statement('SET FOREIGN_KEY_CHECKS=0;');

        // Clear existing data
        $this->call(ClearDataSeeder::class);

        // Seed in correct order to maintain foreign key relationships
        $this->call([
            UserSeeder::class,
            CategorySeeder::class,
            TagSeeder::class,
            PostSeeder::class,
            CommentSeeder::class,
            PostTagSeeder::class,
        ]);

        // Enable foreign key checks
        DB::statement('SET FOREIGN_KEY_CHECKS=1;');
    }
}

User Seeder with Multiple Scenarios

<?php
// database/seeders/UserSeeder.php

namespace Database\Seeders;

use App\Models\User;
use Illuminate\Database\Seeder;
use Illuminate\Support\Facades\Hash;

class UserSeeder extends Seeder
{
    public function run(): void
    {
        // Create specific test users
        $admin = User::create([
            'name' => 'Admin User',
            'email' => 'admin@teachyleaf.com',
            'password' => Hash::make('password123'),
            'is_admin' => true,
            'email_verified_at' => now(),
        ]);

        $author = User::create([
            'name' => 'Content Author',
            'email' => 'author@teachyleaf.com',
            'password' => Hash::make('password123'),
            'email_verified_at' => now(),
        ]);

        $moderator = User::create([
            'name' => 'Content Moderator',
            'email' => 'moderator@teachyleaf.com',
            'password' => Hash::make('password123'),
            'role' => 'moderator',
            'email_verified_at' => now(),
        ]);

        // Create random users using factories
        User::factory()->count(50)->create();
        
        // Create users with specific states
        User::factory()->count(5)->admin()->create();
        User::factory()->count(3)->moderator()->create();
        User::factory()->count(2)->unverified()->create();
        User::factory()->count(2)->inactive()->create();
        User::factory()->count(1)->suspended()->create();

        $this->command->info('Users seeded successfully!');
        $this->command->info("Admin: admin@teachyleaf.com / password123");
        $this->command->info("Author: author@teachyleaf.com / password123");
    }
}

Post Seeder with Relationships

<?php
// database/seeders/PostSeeder.php

namespace Database\Seeders;

use App\Models\Post;
use App\Models\User;
use App\Models\Category;
use App\Models\Tag;
use Illuminate\Database\Seeder;

class PostSeeder extends Seeder
{
    public function run(): void
    {
        // Get all users and categories for relationship assignment
        $users = User::all();
        $categories = Category::all();
        $tags = Tag::all();

        // Create specific featured posts
        $featuredPosts = [
            [
                'title' => 'Getting Started with Laravel',
                'content' => $this->generateContent('Laravel'),
                'user_id' => $users->where('email', 'author@teachyleaf.com')->first()->id,
                'is_featured' => true,
                'is_published' => true,
            ],
            [
                'title' => 'Advanced Eloquent Relationships',
                'content' => $this->generateContent('Eloquent'),
                'user_id' => $users->where('email', 'author@teachyleaf.com')->first()->id,
                'is_featured' => true,
                'is_published' => true,
            ],
        ];

        foreach ($featuredPosts as $postData) {
            $post = Post::create(array_merge($postData, [
                'slug' => \Illuminate\Support\Str::slug($postData['title']),
                'excerpt' => \Illuminate\Support\Str::limit(strip_tags($postData['content']), 150),
                'published_at' => now(),
                'category_id' => $categories->random()->id,
            ]));

            // Attach random tags
            $post->tags()->attach(
                $tags->random(rand(2, 5))->pluck('id')->toArray()
            );
        }

        // Create random posts using factories
        $posts = Post::factory()
                    ->count(100)
                    ->create([
                        'user_id' => fn() => $users->random()->id,
                        'category_id' => fn() => $categories->random()->id,
                    ]);

        // Attach tags to all posts
        foreach ($posts as $post) {
            $post->tags()->attach(
                $tags->random(rand(1, 4))->pluck('id')->toArray()
            );
        }

        // Create some draft posts
        Post::factory()
            ->count(15)
            ->draft()
            ->create([
                'user_id' => fn() => $users->random()->id,
                'category_id' => fn() => $categories->random()->id,
            ]);

        $this->command->info('Posts seeded successfully!');
    }

    private function generateContent(string $topic): string
    {
        $paragraphs = [
            "This is a comprehensive guide about {$topic}. In this article, we'll explore the fundamental concepts and advanced techniques.",
            "Understanding {$topic} is crucial for modern web development. It provides powerful features that make development faster and more efficient.",
            "Many developers find {$topic} intimidating at first, but with proper guidance, it becomes an invaluable tool in your development toolkit.",
            "Let's dive deeper into the practical applications of {$topic}. We'll look at real-world examples and best practices.",
            "One of the key benefits of {$topic} is its flexibility. You can adapt it to various use cases and project requirements.",
            "As we continue our exploration of {$topic}, remember that practice is essential. Try out the examples and experiment with your own code.",
            "In conclusion, mastering {$topic} will significantly improve your development workflow and application performance."
        ];

        $content = '';
        foreach ($paragraphs as $index => $paragraph) {
            if ($index % 2 === 0 && $index > 0) {
                $content .= "<h3>" . fake()->sentence(4) . "</h3>\n\n";
            }
            $content .= "<p>{$paragraph}</p>\n\n";
        }

        return $content;
    }
}

Comment Seeder with Polymorphic Relationships

<?php
// database/seeders/CommentSeeder.php

namespace Database\Seeders;

use App\Models\Comment;
use App\Models\Post;
use App\Models\Video;
use App\Models\User;
use Illuminate\Database\Seeder;

class CommentSeeder extends Seeder
{
    public function run(): void
    {
        $users = User::all();
        $posts = Post::published()->get();
        $videos = Video::all();

        // Create comments for posts
        foreach ($posts as $post) {
            $commentCount = rand(0, 15);
            
            Comment::factory()
                   ->count($commentCount)
                   ->forPost($post)
                   ->create([
                       'user_id' => fn() => $users->random()->id,
                   ]);
        }

        // Create comments for videos
        foreach ($videos as $video) {
            $commentCount = rand(0, 10);
            
            Comment::factory()
                   ->count($commentCount)
                   ->forVideo($video)
                   ->create([
                       'user_id' => fn() => $users->random()->id,
                   ]);
        }

        // Create some nested comments (replies)
        $comments = Comment::all();
        foreach ($comments->random(20) as $parentComment) {
            Comment::factory()
                   ->count(rand(1, 3))
                   ->create([
                       'user_id' => fn() => $users->random()->id,
                       'parent_id' => $parentComment->id,
                       'commentable_type' => $parentComment->commentable_type,
                       'commentable_id' => $parentComment->commentable_id,
                   ]);
        }

        $this->command->info('Comments seeded successfully!');
    }
}

Using Factories in Tests

Basic Factory Usage in Tests

<?php
// tests/Feature/PostTest.php

namespace Tests\Feature;

use App\Models\Post;
use App\Models\User;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Tests\TestCase;

class PostTest extends TestCase
{
    use RefreshDatabase;

    public function test_user_can_create_post(): void
    {
        // Create a user using factory
        $user = User::factory()->create();

        // Create post data
        $postData = [
            'title' => 'Test Post',
            'content' => 'This is a test post content.',
        ];

        // Act as the user and create post
        $response = $this->actingAs($user)
                        ->post('/posts', $postData);

        // Assertions
        $response->assertRedirect('/posts');
        $this->assertDatabaseHas('posts', [
            'title' => 'Test Post',
            'user_id' => $user->id,
        ]);
    }

    public function test_published_posts_are_visible(): void
    {
        // Create published and draft posts
        $publishedPost = Post::factory()->published()->create();
        $draftPost = Post::factory()->draft()->create();

        // Access posts index
        $response = $this->get('/posts');

        // Assert only published posts are visible
        $response->assertSee($publishedPost->title);
        $response->assertDontSee($draftPost->title);
    }

    public function test_post_with_comments(): void
    {
        // Create a post with 5 comments
        $post = Post::factory()
                   ->hasComments(5)
                   ->create();

        $this->assertCount(5, $post->comments);
    }

    public function test_user_with_posts_and_comments(): void
    {
        // Create user with 3 posts, each with 2 comments
        $user = User::factory()
                   ->hasPosts(3, function (array $attributes, User $user) {
                       return ['user_id' => $user->id];
                   })
                   ->hasComments(2)
                   ->create();

        $this->assertCount(3, $user->posts);
        $this->assertCount(2, $user->comments);
    }
}

Advanced Testing Scenarios

<?php
// tests/Feature/AdvancedPostTest.php

namespace Tests\Feature;

use App\Models\Post;
use App\Models\User;
use App\Models\Tag;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Tests\TestCase;

class AdvancedPostTest extends TestCase
{
    use RefreshDatabase;

    public function test_post_with_specific_tags(): void
    {
        // Create tags
        $laravelTag = Tag::factory()->create(['name' => 'Laravel']);
        $phpTag = Tag::factory()->create(['name' => 'PHP']);

        // Create post with specific tags
        $post = Post::factory()
                   ->hasAttached([$laravelTag, $phpTag])
                   ->create();

        $this->assertCount(2, $post->tags);
        $this->assertTrue($post->tags->contains('name', 'Laravel'));
    }

    public function test_multiple_users_with_different_roles(): void
    {
        // Create users with different roles
        $admin = User::factory()->admin()->create();
        $moderator = User::factory()->moderator()->create();
        $regularUser = User::factory()->create();

        // Create posts for each user
        $adminPost = Post::factory()->create(['user_id' => $admin->id]);
        $moderatorPost = Post::factory()->create(['user_id' => $moderator->id]);
        $userPost = Post::factory()->create(['user_id' => $regularUser->id]);

        // Test access control
        $this->actingAs($admin)
             ->get('/admin/posts')
             ->assertStatus(200);

        $this->actingAs($regularUser)
             ->get('/admin/posts')
             ->assertStatus(403);
    }

    public function test_factory_states_combination(): void
    {
        // Create a featured, published post from last week
        $post = Post::factory()
                   ->published()
                   ->featured()
                   ->create([
                       'published_at' => now()->subWeek(),
                   ]);

        $this->assertTrue($post->is_published);
        $this->assertTrue($post->is_featured);
        $this->assertTrue($post->published_at->lt(now()));
    }
}

Performance Optimization for Seeding

Efficient Mass Insertion

<?php
// database/seeders/PerformancePostSeeder.php

namespace Database\Seeders;

use App\Models\Post;
use App\Models\User;
use Illuminate\Database\Seeder;
use Illuminate\Support\Facades\DB;

class PerformancePostSeeder extends Seeder
{
    public function run(): void
    {
        $users = User::all();
        $batchSize = 1000;
        $totalPosts = 10000;

        $this->command->info("Seeding {$totalPosts} posts...");
        $this->command->getOutput()->progressStart($totalPosts);

        for ($i = 0; $i < $totalPosts; $i += $batchSize) {
            $posts = [];
            
            for ($j = 0; $j < $batchSize && ($i + $j) < $totalPosts; $j++) {
                $title = fake()->sentence(6);
                
                $posts[] = [
                    'title' => $title,
                    'slug' => \Illuminate\Support\Str::slug($title),
                    'excerpt' => fake()->paragraph(2),
                    'content' => fake()->paragraphs(5, true),
                    'user_id' => $users->random()->id,
                    'is_published' => fake()->boolean(80),
                    'published_at' => fake()->dateTimeBetween('-1 year', 'now'),
                    'created_at' => now(),
                    'updated_at' => now(),
                ];

                $this->command->getOutput()->progressAdvance();
            }

            // Use insert for better performance (bypasses model events)
            DB::table('posts')->insert($posts);
        }

        $this->command->getOutput()->progressFinish();
        $this->command->info("Seeded {$totalPosts} posts successfully!");
    }
}

Common Interview Questions & Answers

1. What's the difference between seeders and factories?

Seeders are used to populate the database with specific data, while factories define how to generate fake data for models. Factories are more flexible and are often used within seeders.

2. How do you handle relationships in factories?

Use closure callbacks in factory definitions: 'user_id' => User::factory() or use relationship methods like has() and for() when creating models.

3. What is Faker and what can it generate?

Faker is a PHP library that generates fake data. It can create names, addresses, text, dates, numbers, emails, and much more for realistic test data.

4. How do you seed data in production?

Be very careful! Only seed essential data (like admin users, basic categories) and always use real, secure passwords. Never use Faker in production seeding.

5. What are factory states?

Factory states allow you to define variations of your base factory, like admin(), unverified(), or published() to create models with specific attribute sets.

6. How do you optimize seeding for large datasets?

Use DB::table()->insert() for mass insertion, disable model events, use transactions, and seed in batches to improve performance with large datasets.

Best Practices

  • Use meaningful data that reflects real-world scenarios
  • Maintain referential integrity by seeding in the correct order
  • Use factories in tests for consistent, isolated test data
  • Avoid seeding in production unless for essential setup data
  • Use environment checks to prevent accidental production seeding
  • Document your seeders so others understand the data structure
  • Use specific test users with known credentials for manual testing
  • Consider performance when seeding large datasets

Useful Artisan Commands

# Seeding commands
php artisan db:seed
php artisan db:seed --class=UserSeeder
php artisan migrate:fresh --seed
php artisan migrate:refresh --seed

# Factory commands (in Tinker)
User::factory()->count(10)->create()
User::factory()->admin()->create()
Post::factory()->count(5)->forUser($user)->create()

# Make commands
php artisan make:seeder UserSeeder
php artisan make:factory UserFactory
php artisan make:factory PostFactory --model=Post

You're now equipped to populate your Laravel applications with realistic, useful test data using seeders and factories! In our next post, we'll explore Laravel Middleware: How to Filter HTTP Requests (With Examples) to learn how to intercept and filter HTTP requests.