Skip to main content
Create your own
Lesson illustration

Database Seeding with Factories and Seeders

Hello! Welcome to the final lesson in our module on Database Schema Design & Migrations.

In our previous lesson, we meticulously built the "scaffolding" for our database. We created the tables, defined their columns, and established the critical relationships between them using foreign keys and indexes. Our database schema is now robust and optimized, but it's also completely empty.

This lesson bridges the gap between an empty schema and a functional development environment. Our goal is to seed the database with realistic test data using model factories and seeders. By the end of this session, you'll be able to:

  • Generate large amounts of fake, yet realistic, data for any model using Model Factories.
  • Control and orchestrate the data population process using Database Seeders.
  • Seamlessly seed complex relationships, ensuring your test data mirrors a real-world application.
  • Reset and repopulate your entire database with a single command.

This is an essential skill for efficient development, allowing you to test features, build UIs, and optimize queries without manually entering data.

1. The Tools of the Trade: Factories and Seeders

To populate our database, Laravel provides two distinct but related tools:

  • Model Factories: Think of a factory as a blueprint or a recipe for a single model instance. It defines what a fake User or Product should look like, using the Faker library to generate realistic-looking data (names, emails, prices, etc.).
  • Database Seeders: A seeder is the script that executes these blueprints. It's the "chef" that takes the recipes from the factories and populates the database tables with data.

Let's start with the blueprints.

2. Model Factories: Crafting Realistic Data

A model factory is a class that corresponds to a single Eloquent model. Its primary job is to return an array defining the model's default attributes.

30 Days to Learn Laravel, Ep 10 - Model Factories

To begin, let's get a clear understanding of what model factories are and why they are so valuable in a typical development workflow. This video from Laracasts provides a perfect introduction.

Watch the first 2 minutes of the video (00:09 - 02:00). Pay close attention to the two main use cases mentioned: setting up a local development environment and preparing for automated tests.

Creating and Using a Factory

To use factories, your model must use the Illuminate\Database\Eloquent\Factories\HasFactory trait. Artisan-generated models include this by default.

You can create a new factory using the make:factory Artisan command:

php artisan make:factory ProductFactory --model=Product

This creates a database/factories/ProductFactory.php file. Inside, the definition() method is where you'll define the fake data using the fake() helper, which is an instance of the Faker library.

// database/factories/ProductFactory.php

public function definition(): array
{
    return [
        'name' => fake()->words(3, true), // e.g., "Sleek Cotton Computer"
        'description' => fake()->paragraph(),
        'price' => fake()->randomFloat(2, 10, 500), // 2 decimal places, between 10 and 500
        'stock_quantity' => fake()->numberBetween(0, 100),
    ];
}

Once defined, you can use tinker to quickly generate data.

30 Days to Learn Laravel, Ep 10 - Model Factories

Now, let's see factories in action. This next clip demonstrates how to use tinker to create records, how to customize a factory's definition, and how to generate multiple records at once.

Watch from 02:00 to 07:36. Note the syntax Model::factory()->count(50)->create() and how a new factory is generated and defined for a custom model.

The dev.to article, "Laravel Seeders & Factories," provides a handy cheat sheet of common Faker methods that you can refer to.

Laravel Seeders & Factories - Faker Cheat Sheet

For a quick reference of available Faker methods, check out this cheat sheet.

Scan the section '4. Faker Cheat Sheet — All Available Methods' to get a feel for the variety of data you can generate, from personal info to text and numbers.

Seeding Relationships

Our application's power comes from its relationships. A Product belongs to a Category, and an Order belongs to a User. Our fake data must reflect this.

Laravel 9 Seeding Table with Relationships Code Snippet
This image illustrates how factories can be nested to create related data, such as an artist with their albums and songs, in a single, fluent operation.

The simplest way to handle a belongsTo relationship is to call the related model's factory directly within the definition.

// In ProductFactory.php
use App\Models\Category;

public function definition(): array
{
    return [
        'name' => fake()->words(3, true),
        // ... other attributes
        'category_id' => Category::factory(), // This will create a new Category for each Product!
    ];
}

This is just the beginning. Laravel offers a powerful, expressive API for seeding all types of relationships.

30 Days to Learn Laravel, Ep 10 - Model Factories

Let's watch a practical example of modifying a factory to account for a new relationship. This clip shows how to handle a belongsTo relationship by nesting a factory call.

Watch from 11:21 to 18:28. The key takeaway is seeing how adding an employer_id to the JobFactory works by calling Employer::factory(). This ensures that every fake job has a corresponding fake employer.

While nesting factories is great, Laravel also provides has() and for() methods which are often cleaner for defining one-to-many or many-to-many relationships from within a seeder.

How to Configure Database Seeding in Laravel - Seeding Relationships

For a comprehensive guide on seeding various relationship types, this article from OneUptime is an excellent resource. It provides clear code examples for the exact scenarios in our schema.

Read the section 'Seeding Relationships'. Focus on the 'One-to-Many' and 'Many-to-Many' examples, as they are directly applicable to our User/Order and Product/Category relationships.

3. Database Seeders: Executing the Plan

We now have the blueprints (factories). Next, we need to create the seeder scripts that will run them.

A seeder is a simple class with one method: run(). Your main entry point is database/seeders/DatabaseSeeder.php.

30 Days to Learn Laravel, Ep 15 - Understanding Database Seeders

This video explains the role of seeders and demonstrates the basic commands to run them, including the incredibly useful migrate:fresh --seed command.

Watch from the beginning to 02:54. Understand that seeders provide a repeatable way to populate the database after a migration, and that migrate:fresh --seed automates this entire process.

Organizing Your Seeders

While you can put all your logic in DatabaseSeeder.php, it's best practice to create separate seeder classes for each model. This keeps your code organized and allows you to seed parts of your database in isolation.

You can create a new seeder with make:seeder:

php artisan make:seeder ProductSeeder

Then, you call this seeder from your main DatabaseSeeder:

// In DatabaseSeeder.php
public function run(): void
{
    $this->call([
        CategorySeeder::class,
        ProductSeeder::class,
        UserSeeder::class,
        OrderSeeder::class,
    ]);
}

Order is important! You must seed tables that are on the "one" side of a relationship before the "many" side (e.g., seed Categories before Products).

30 Days to Learn Laravel, Ep 15 - Understanding Database Seeders

Let's see how to create and use these separate seeder classes for better organization.

Watch from 03:59 to 05:53. The key concept is using php artisan make:seeder and then registering the new seeder class inside DatabaseSeeder's $this->call() method.

Your Turn: Let's Populate the Database

Now it's time to put all this theory into practice.

Step 1: Create the Factories

Generate factories for all your main models: Category, Product, User, and Order.

php artisan make:factory CategoryFactory --model=Category
php artisan make:factory ProductFactory --model=Product



# UserFactory already exists
php artisan make:factory OrderFactory --model=Order

Fill in the definition() method for each, using Faker to generate appropriate data. For the relationship fields:

  • ProductFactory: Set 'category_id' => Category::factory().
  • OrderFactory: Set 'user_id' => User::factory().

Step 2: Create the Seeders

Generate a seeder for each model.

php artisan make:seeder CategorySeeder
php artisan make:seeder ProductSeeder
php artisan make:seeder UserSeeder
php artisan make:seeder OrderSeeder

Step 3: Implement the Seeder Logic

In each seeder's run() method, call the corresponding factory.

// In database/seeders/CategorySeeder.php
use App\Models\Category;

public function run(): void
{
    // Create 10 categories
    Category::factory()->count(10)->create();
}

// In database/seeders/ProductSeeder.php
use App\Models\Product;
use App\Models\Category;

public function run(): void
{
    // Get all existing categories
    $categories = Category::all();
    
    // Create 50 products, recycling the existing categories
    Product::factory()->count(50)->recycle($categories)->create();
}
  • Challenge: Implement the UserSeeder and OrderSeeder. In the OrderSeeder, use the recycle() method (as shown in the ProductSeeder above) to assign orders to the users you just created.
  • For the pivot tables (category_product, order_product), you can use the hasAttached factory method after creating the primary models. For example, when creating products, you can attach them to random categories.

Step 4: Orchestrate in DatabaseSeeder

Open database/seeders/DatabaseSeeder.php and update the run() method to call your new seeders in the correct order.

// In DatabaseSeeder.php
public function run(): void
{
    $this->call([
        CategorySeeder::class,
        UserSeeder::class,
        ProductSeeder::class,
        OrderSeeder::class,
    ]);

    // Now, let's handle the many-to-many pivot tables.
    $products = \App\Models\Product::all();
    \App\Models\Category::all()->each(function ($category) use ($products) {
        $category->products()->attach(
            $products->random(rand(5, 15))->pluck('id')->toArray()
        );
    });

    $orders = \App\Models\Order::all();
    $orders->each(function ($order) use ($products) {
        $order->products()->attach(
            $products->random(rand(1, 5))->pluck('id')->toArray(),
            ['quantity' => rand(1, 3)] // Attaching pivot data
        );
    });
}

Step 5: Run It!

Execute the master command to drop all tables, re-run all migrations, and run all your seeders.

php artisan migrate:fresh --seed

After the command finishes, connect to your MSSQL database. You should see all your tables filled with thousands of rows of realistic-looking data!

Conclusion

Fantastic work! You have successfully built a complete, relational database schema and populated it with a rich set of test data. This is a massive step forward in our development process.

Key Takeaways:

  • Factories are blueprints for creating single model instances with fake data via the Faker library.
  • Seeders are executable scripts that call factories to populate the database.
  • It's best practice to create separate seeder classes for each model and orchestrate them from DatabaseSeeder.php.
  • Relationships are seeded by nesting factory calls or using powerful methods like has(), for(), and recycle().
  • The php artisan migrate:fresh --seed command is your most powerful tool for maintaining a clean, consistent, and populated development database.

Next Up:

Our database is no longer an empty shell; it's a living environment full of data. In the next module, "Advanced Eloquent ORM," we will start interacting with this data in sophisticated ways. We'll begin by learning how to apply accessors, mutators, and attribute casting to transform model data as it's being set or retrieved, making your models even more powerful and intelligent.

Can't find a good explanation? Sign up and we'll make it for you

Sign up