Model Factories
A factory invents plausible rows for one model, with named states and relationships, for tests and seeders.
Post::factory()->count(3)->published()->for(User::factory())->create();A model factory is a class that knows how to invent one plausible row for a model. You call it from a test or a seeder, get a real model back, and stop writing twelve-line arrays of placeholder data that break whenever a column is added.
Making one
php artisan make:factory PostFactory writes database/factories/PostFactory.php. Its definition(): array method returns column values, and fake() returns the Faker generator used to produce them. The factory() method itself comes from the Illuminate\Database\Eloquent\Factories\HasFactory trait on the model, not from Eloquent. The factory is matched to a model by name: Database\Factories\PostFactory looks for App\Models\Post. When the names do not line up, say so with protected $model = Post::class; or, in Laravel 13, the #[UseModel(Post::class)] attribute on the factory class.
States and relationships
A state is a named override returning extra attributes, built from $this->state(...) and then chained: Post::factory()->published()->create(). Post::factory(3) is shorthand for Post::factory()->count(3). ->for(User::factory()) fills a belongsTo parent, ->has(Comment::factory()->count(5)) creates children. create() persists and returns saved models; make() instantiates without touching the database.
Factories are development tooling. fakerphp/faker sits in require-dev, so a factory called from production code fails on a server installed with --no-dev: the factory class loads, Faker does not.
Example
<?php
// database/factories/PostFactory.php
declare(strict_types=1);
namespace Database\Factories;
use App\Models\Post;
use App\Models\User;
use Illuminate\Database\Eloquent\Factories\Factory;
use Illuminate\Support\Str;
/**
* @extends Factory<Post>
*/
class PostFactory extends Factory
{
/**
* Define the model's default state.
*
* @return array<string, mixed>
*/
public function definition(): array
{
$title = fake()->unique()->sentence(6);
return [
// A Factory given as an attribute value is resolved first and
// its key used here, so every post gets an author.
'user_id' => User::factory(),
'title' => $title,
'slug' => Str::slug($title),
'body' => fake()->paragraphs(3, true),
'published_at' => null,
];
}
/**
* A post that is live on the site.
*/
public function published(): static
{
return $this->state(fn (array $attributes) => [
'published_at' => fake()->dateTimeBetween('-1 year'),
]);
}
/**
* A post whose title is awkward on purpose.
*/
public function withMessyTitle(): static
{
return $this->state(function (array $attributes) {
$title = ' Hello / World -- '.fake()->word().' ';
return [
'title' => $title,
'slug' => Str::slug($title),
];
});
}
}When to use it
- Create a user with three published posts in one line for a test.
- Fill a local database with realistic content without writing insert statements.
- Produce one record per status so a filter or grouping can be checked.
- Build an unsaved model with make() to test validation without a write.
More examples
Building a graph of records
has() and for() are the two directions of a relationship: has() creates records on the far side of a hasMany or hasOne, for() attaches the one record a belongsTo points at. The relationship name is inferred from the related model and must exist as a method on the model, so hasPosts() requires a posts() relationship on User. Passing the name as the second argument is how you handle relationships whose names do not match.
<?php
use App\Models\Comment;
use App\Models\Post;
use App\Models\User;
// Ten users, each with three published posts, each post with two comments.
User::factory(10)
->has(Post::factory()->count(3)->published()->has(Comment::factory()->count(2)))
->create();
// hasPosts() and forUser() are magic shorthands: Factory::__call strips
// the has/for prefix, camel-cases the rest, and treats it as the
// relationship name on the model. These two lines are the same thing.
User::factory()->hasPosts(3)->create();
User::factory()->has(Post::factory()->count(3), 'posts')->create();
// One known parent, many children: pass the model, not a factory.
$author = User::factory()->create(['name' => 'Ada Lovelace']);
Post::factory(5)->for($author)->published()->create();
// make() returns unsaved models. Useful when you only need the object.
$draft = Post::factory()->make(['title' => 'Not saved anywhere']);Pointing a factory and a model at each other
This is the magic worth naming, because the error when it misfires is a confusing class-not-found on a model you never mentioned. Resolution order inside the factory is: the #[UseModel] attribute, then the $model property, then the name guess. On the model side, $factory and #[UseFactory] both override the guess, and both still need the HasFactory trait, since that is what defines factory() and reads the attribute.
<?php
// Convention: Database\Factories\PostFactory -> App\Models\Post.
// Nothing to configure while the names match.
// --- When the factory name does not imply the model ---
namespace Database\Factories;
use App\Models\Post;
use Illuminate\Database\Eloquent\Factories\Attributes\UseModel;
use Illuminate\Database\Eloquent\Factories\Factory;
// Laravel 13 attribute form
#[UseModel(Post::class)]
class ArticleFactory extends Factory
{
// Longhand, still supported and identical in effect:
// protected $model = Post::class;
public function definition(): array
{
return ['title' => fake()->sentence()];
}
}
// --- When the model should name its factory ---
namespace App\Models;
use Database\Factories\ArticleFactory;
use Illuminate\Database\Eloquent\Attributes\UseFactory;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
#[UseFactory(ArticleFactory::class)]
class Post extends Model
{
use HasFactory;
// Longhand:
// protected static string $factory = ArticleFactory::class;
}Varying one attribute across a batch
Reach for sequence() when a test needs one record in each state rather than three random ones, which is most tests that filter or group. The closure form receives a sequence object whose index property is the zero-based position, so you can build predictable values. Each call returns a new factory instance, so the original factory is unchanged and safe to reuse.
<?php
use App\Models\Invoice;
// Four invoices, one per status, cycling in order.
Invoice::factory()
->count(4)
->sequence(
['status' => 'draft'],
['status' => 'sent'],
['status' => 'paid'],
['status' => 'void'],
)
->create();
// sequence() cycles if count exceeds the number of entries: 10 invoices
// here would be draft, sent, paid, void, draft, ... in that order.
// Deterministic ids are easier to assert on than random ones.
Invoice::factory()
->count(3)
->sequence(fn ($sequence) => ['number' => 'INV-'.(1000 + $sequence->index)])
->create();
Discussion