Currently Available: Need a skilled Software Developer for your next project?
Categories
Laravel

Laravel Factories

In Laravel, a Factory is a class dedicated to generating instances of your Eloquent models. They solve a fundamental problem: your application needs data to function, especially during testing and development, and creating that data manually is tedious, repetitive, and error-prone.

Think of it like a manufacturing assembly line for your data.

  • The Blueprint: The factory holds the blueprint for a "standard" product. For a User model, the blueprint might specify that every user must have a name, a unique email address, and a hashed password. This blueprint is the definition() method in your factory class.
  • The Production Command: You issue commands to the assembly line.
    • "Build one standard product." -> User::factory()->create()
    • "Build 50 standard products." -> User::factory()->count(50)->create()
  • Customization: You can give the assembly line a special instruction for a single run.
    • "Build one standard product, but make sure this one is an administrator." -> User::factory()->create(['is_admin' => true])

In essence, a Laravel Factory provides a centralized, repeatable, and fluent API for constructing and persisting model objects, primarily for automated tests and database seeding.


How a Factory Works

1. Creation and Structure

You create a factory using an Artisan command. For a Post model, you run:

php artisan make:factory PostFactory --model=Post

This generates database/factories/PostFactory.php, which looks like this:

<?php

namespace Database\Factories;

use Illuminate\Database\Eloquent\Factories\Factory;

class PostFactory extends Factory
{
    /**
     * The name of the factory's corresponding model.
     *
     * @var string
     */
    protected $model = \App\Models\Post::class; // Links this factory to the Post model

    /**
     * Define the model's default state.
     *
     * @return array<string, mixed>
     */
    public function definition(): array
    {
        // This is the core blueprint for a Post model.
        return [
            // 'database_column_name' => value,
        ];
    }
}

2. The definition() Method

This method is the heart of the factory. You define the default attributes for your model here. To generate realistic fake data, Laravel integrates the Faker library.

Here is a filled-out definition() for a Post model:

public function definition(): array
{
    return [
        'title'        => $this->faker->sentence(6), // Generates a random sentence with 6 words.
        'content'      => $this->faker->paragraphs(3, true), // Generates 3 paragraphs as a single string.
        'is_published' => $this->faker->boolean(75), // 75% chance of being `true`.
        'created_at'   => $this->faker->dateTimeBetween('-1 year', 'now'), // A random date in the last year.
    ];
}

$this->faker gives you access to dozens of data generators for names, addresses, text, numbers, dates, and more.


Core Usage

create() vs. make()

This is a critical distinction:

  • Post::factory()->create(): Creates a model instance AND persists it to the database. This is what you use in 99% of tests and all database seeders.

  • Post::factory()->make(): Creates a model instance but does NOT save it to the database. The object only exists in memory. This is useful for testing logic that happens before a model is saved, like validation requests.

Generating Models

// Generate and save one Post to the database with default attributes
$post = Post::factory()->create();

// Generate and save 10 Posts
$posts = Post::factory()->count(10)->create();

// Generate one Post, but override the default 'title'
$specificPost = Post::factory()->create([
    'title' => 'My Specific Test Title',
]);

// Generate an in-memory instance without saving it
$unsavedPost = Post::factory()->make();

Advanced Features

1. Relationships

This is where factories become extremely powerful. If a Post belongs to a User, you can define that relationship directly in the factory.

In PostFactory.php:

use App\Models\User;

public function definition(): array
{
    return [
        'user_id' => User::factory(), // <-- This is the key part
        'title'   => $this->faker->sentence(),
        'content' => $this->faker->paragraph(),
    ];
}

When you now run Post::factory()->create(), Laravel's factory system will:

  1. See that user_id needs a User model.
  2. Automatically execute the UserFactory to create a new User.
  3. Get the ID from that newly created User.
  4. Assign that ID to the user_id on the Post being created.
  5. Persist both the User and the Post to the database.

Generating models with their relationships:

// Create a User and 5 posts that belong to that user.
// Assumes a `posts()` relationship method exists on the User model.
$userWithPosts = User::factory()
    ->hasPosts(5) // Uses the magic "has<RelationshipName>" method
    ->create();

// Alternatively, a more explicit syntax:
$userWithPosts = User::factory()
    ->has(Post::factory()->count(5))
    ->create();

2. States

States are predefined modifications to your factory's default definition. They are used to represent common variations of a model. For example, a post can be a "draft" or "published".

In PostFactory.php, define a state:

class PostFactory extends Factory
{
    // ... definition() method ...

    /**
     * Define a "published" state for the post.
     */
    public function published(): static
    {
        return $this->state(fn (array $attributes) => [
            'is_published' => true,
            'published_at' => now(),
        ]);
    }
}

Using the state:
This makes your tests and seeders much more readable.

// Create a default post (is_published is random)
Post::factory()->create();

// Create a post that is explicitly published
Post::factory()->published()->create();

// Create 5 published posts and 10 draft posts
Post::factory()->count(5)->published()->create();
Post::factory()->count(10)->create(['is_published' => false]); // a draft via overriding
What I'm building

Delegate tasks. Get software.

Give Vroni a GitHub issue, bug report, spec, or rough idea. It reads the repo, plans the change, writes code, runs checks, and works toward a review-ready pull request.

Take a look at vroni.com

Subscribe to my newsletter

Get new posts when I publish them.

I respect your privacy. Unsubscribe at any time.

Leave a Reply

Your email address will not be published. Required fields are marked *