Database Seeding & Factories
Database Seeding & Factories
Database seeding and model factories are essential tools for populating your database with test data. They're invaluable for development, testing, and demonstrations. In this lesson, you'll learn how to generate realistic fake data efficiently.
Why Use Seeders and Factories?
Seeders and factories allow you to:
- Quickly populate your database with test data
- Create consistent data across development environments
- Generate realistic data for testing and demos
- Reset your database to a known state
- Test your application with various data scenarios
Introduction to Model Factories
Model factories use the Faker library to generate realistic fake data. Each model can have a factory that defines its default state.
Creating a Factory
# Create a factory for a model
php artisan make:factory ProductFactory
# Create a factory and specify the model
php artisan make:factory ProductFactory --model=Product
This creates a factory file in database/factories/ProductFactory.php:
<?php
namespace Database\Factories;
use Illuminate\Database\Eloquent\Factories\Factory;
class ProductFactory extends Factory
{
/**
* Define the model's default state.
*/
public function definition(): array
{
return [
'name' => fake()->words(3, true),
'description' => fake()->paragraph(),
'price' => fake()->randomFloat(2, 10, 1000),
'quantity' => fake()->numberBetween(0, 100),
'is_active' => fake()->boolean(80), // 80% chance of true
'category' => fake()->randomElement(['Electronics', 'Clothing', 'Food', 'Books']),
];
}
}
Faker Library - Common Methods
Faker provides hundreds of methods for generating realistic fake data:
// Personal Information
fake()->name() // 'John Doe'
fake()->firstName() // 'John'
fake()->lastName() // 'Doe'
fake()->email() // 'john@example.com'
fake()->safeEmail() // Safe testing emails
fake()->phoneNumber() // '+1-555-123-4567'
// Address
fake()->address() // Full address
fake()->city() // 'New York'
fake()->country() // 'United States'
fake()->postcode() // '12345'
fake()->streetAddress() // '123 Main St'
// Text
fake()->word() // Single random word
fake()->words(3) // Array of 3 words
fake()->words(3, true) // String of 3 words
fake()->sentence() // Random sentence
fake()->paragraph() // Random paragraph
fake()->text(200) // Text with max 200 chars
// Numbers
fake()->randomNumber(5) // 5-digit number
fake()->numberBetween(1, 100) // Number between 1-100
fake()->randomFloat(2, 0, 100) // Float with 2 decimals
// Dates
fake()->date() // '2023-05-15'
fake()->dateTime() // DateTime object
fake()->dateTimeBetween('-1 year', 'now')
fake()->time() // '14:30:00'
// Internet
fake()->url() // 'https://example.com'
fake()->slug() // 'lorem-ipsum-dolor'
fake()->ipv4() // '192.168.1.1'
fake()->userAgent() // Browser user agent
// Files & Images
fake()->imageUrl(640, 480) // Image URL
fake()->fileExtension() // 'jpg'
fake()->mimeType() // 'image/jpeg'
// Boolean & Random
fake()->boolean() // true or false (50/50)
fake()->boolean(70) // 70% chance of true
fake()->randomElement(['A', 'B', 'C']) // Pick from array
fake()->randomElements(['A', 'B', 'C'], 2) // Pick 2
// Unique & Optional
fake()->unique()->email() // Unique email
fake()->optional()->email() // Email or null
Using Factories
Once you've defined a factory, you can use it to generate models:
<?php
use App\Models\Product;
// Create a single product
$product = Product::factory()->create();
// Create multiple products
$products = Product::factory()->count(50)->create();
// Make without saving to database
$product = Product::factory()->make();
// Create with custom attributes
$product = Product::factory()->create([
'name' => 'Custom Product',
'price' => 99.99,
]);
// Create multiple with custom attributes
$products = Product::factory()->count(10)->create([
'category' => 'Electronics',
]);
Factory States
States allow you to define variations of your factory data:
<?php
namespace Database\Factories;
use Illuminate\Database\Eloquent\Factories\Factory;
class ProductFactory extends Factory
{
public function definition(): array
{
return [
'name' => fake()->words(3, true),
'price' => fake()->randomFloat(2, 10, 1000),
'quantity' => fake()->numberBetween(0, 100),
'is_active' => true,
];
}
/**
* Indicate that the product is out of stock.
*/
public function outOfStock(): static
{
return $this->state(fn (array $attributes) => [
'quantity' => 0,
]);
}
/**
* Indicate that the product is inactive.
*/
public function inactive(): static
{
return $this->state(fn (array $attributes) => [
'is_active' => false,
]);
}
/**
* Indicate that the product is expensive.
*/
public function expensive(): static
{
return $this->state(fn (array $attributes) => [
'price' => fake()->randomFloat(2, 500, 5000),
]);
}
}
Using factory states:
// Create out of stock product
$product = Product::factory()->outOfStock()->create();
// Create inactive and expensive product
$product = Product::factory()
->inactive()
->expensive()
->create();
// Create 10 out of stock products
$products = Product::factory()
->count(10)
->outOfStock()
->create();
Factory Relationships
Create models with relationships using factories:
<?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(), // Create a user automatically
'title' => fake()->sentence(),
'content' => fake()->paragraphs(3, true),
'published_at' => fake()->dateTimeBetween('-1 month', 'now'),
];
}
/**
* Indicate that the post is unpublished.
*/
public function unpublished(): static
{
return $this->state(fn (array $attributes) => [
'published_at' => null,
]);
}
}
Creating posts with relationships:
// Create post with new user
$post = Post::factory()->create();
// Create post for existing user
$user = User::find(1);
$post = Post::factory()->create([
'user_id' => $user->id,
]);
// Create user with 5 posts
$user = User::factory()
->has(Post::factory()->count(5))
->create();
// Alternative syntax
$user = User::factory()
->hasPosts(5)
->create();
// Create posts with specific state
$user = User::factory()
->has(Post::factory()->count(3)->unpublished())
->create();
// Many-to-many relationships
$post = Post::factory()
->hasAttached(Tag::factory()->count(3))
->create();
Database Seeders
Seeders are classes that populate your database with data. The main seeder is DatabaseSeeder.php.
Creating a Seeder
# Create a new seeder
php artisan make:seeder ProductSeeder
This creates a seeder file in database/seeders/ProductSeeder.php:
<?php
namespace Database\Seeders;
use App\Models\Product;
use Illuminate\Database\Seeder;
class ProductSeeder extends Seeder
{
/**
* Run the database seeds.
*/
public function run(): void
{
// Create 50 products using factory
Product::factory()->count(50)->create();
// Create specific products
Product::create([
'name' => 'Featured Product',
'description' => 'This is a featured product',
'price' => 149.99,
'quantity' => 100,
'is_active' => true,
]);
// Create products with different states
Product::factory()->count(10)->outOfStock()->create();
Product::factory()->count(5)->expensive()->create();
}
}
The DatabaseSeeder
The main seeder that calls other seeders:
<?php
namespace Database\Seeders;
use Illuminate\Database\Seeder;
class DatabaseSeeder extends Seeder
{
/**
* Seed the application's database.
*/
public function run(): void
{
// Call specific seeders
$this->call([
UserSeeder::class,
CategorySeeder::class,
ProductSeeder::class,
PostSeeder::class,
TagSeeder::class,
]);
// Or create data directly
\App\Models\User::factory()->count(10)->create();
// Create admin user
\App\Models\User::factory()->create([
'name' => 'Admin User',
'email' => 'admin@example.com',
'is_admin' => true,
]);
}
}
Running Seeders
# Run all seeders (DatabaseSeeder)
php artisan db:seed
# Run a specific seeder
php artisan db:seed --class=ProductSeeder
# Fresh migration with seeding
php artisan migrate:fresh --seed
# Refresh with seeding
php artisan migrate:refresh --seed
migrate:fresh drops all tables and recreates them. Never use this in production! Always backup your database first.
Advanced Seeding Example
<?php
namespace Database\Seeders;
use App\Models\User;
use App\Models\Post;
use App\Models\Tag;
use App\Models\Comment;
use Illuminate\Database\Seeder;
class BlogSeeder extends Seeder
{
public function run(): void
{
// Create tags first
$tags = Tag::factory()->count(10)->create();
// Create 5 users, each with 10 posts
User::factory()
->count(5)
->has(
Post::factory()
->count(10)
->has(Comment::factory()->count(5)) // 5 comments per post
)
->create()
->each(function ($user) use ($tags) {
// Attach random tags to each user's posts
$user->posts->each(function ($post) use ($tags) {
$post->tags()->attach(
$tags->random(rand(1, 3))->pluck('id')
);
});
});
// Create admin user with special posts
$admin = User::factory()->create([
'name' => 'Admin',
'email' => 'admin@blog.com',
'is_admin' => true,
]);
Post::factory()
->count(5)
->create(['user_id' => $admin->id])
->each(function ($post) use ($tags) {
$post->tags()->attach($tags->random(3));
});
}
}
Sequences in Factories
Create sequential data with the sequence() method:
// Alternate between two states
$products = Product::factory()
->count(10)
->sequence(
['is_active' => true],
['is_active' => false],
)
->create();
// Increment values
$products = Product::factory()
->count(5)
->sequence(fn ($sequence) => [
'name' => 'Product ' . ($sequence->index + 1),
'quantity' => ($sequence->index + 1) * 10,
])
->create();
Create a complete e-commerce seeding setup:
- Create factories for: Product, Category, Order, OrderItem
- Products should belong to categories
- Orders should have multiple order items
- Create factory states: featuredProduct, onSale, newArrival
- Create seeders that generate: 10 categories, 100 products, 50 orders
- Ensure each order has 1-5 random products
Create a blog seeding system with complete relationships:
- Create 10 users (1 admin, 9 regular)
- Each regular user has 5-10 posts
- Each post has 3-10 comments
- Create 20 tags and randomly attach 2-5 tags to each post
- Use factory states for: publishedPost, draftPost, featuredPost
- 80% of posts should be published, 20% drafts
Create a UserFactory with realistic data:
- Generate realistic names, emails, phone numbers
- Add avatar URLs using Faker's imageUrl
- Create bio text (2-3 sentences)
- Add birthday (between 18-70 years old)
- Create states: verified, unverified, banned
- Generate 100 users with 70% verified, 25% unverified, 5% banned
Best Practices
- Use Factories for Flexibility: Define base data in factories, customize in seeders.
- Keep Seeders Organized: Create separate seeders for different models or features.
- Use Realistic Data: Faker provides realistic data that helps catch bugs.
- Leverage States: Define common variations as factory states for reusability.
- Test Relationships: Seed related data to test your relationships and queries.
- Version Control Seeders: Commit seeders to share consistent test data across teams.
- Fresh Migrations in Development: Use
migrate:fresh --seedto reset your database.
Summary
In this lesson, you've learned:
- Creating model factories for generating fake data
- Using Faker library for realistic test data
- Defining factory states for data variations
- Creating models with relationships using factories
- Writing database seeders to populate data
- Running seeders and managing test data
- Advanced seeding techniques for complex relationships
Seeders and factories are essential tools for development and testing. Master them, and you'll save countless hours setting up test environments!