مقاله

Factory state، relation در Factory، Seeder ترتیبی و database seeding برای تست و staging — بدون داده تصادفی بی‌معنا.

Factory و Seeder پیشرفته در Laravel — داده تست واقع‌گرایانه

Factory و Seeder پیشرفته در Laravel — داده تست واقع‌گرایانه

Factory فقط برای «ساختن ۱۰ تا رکورد random» نیست — ابزار اصلی تست، staging و demo است. وقتی Post::factory()->published()->for($user)->create() را بلد باشید، feature testها سریع و خوانا می‌شوند. Seeder هم برای محیط local و portfolio-profile این پروژه همان الگو را دنبال می‌کند.

ساخت Factory

php artisan make:factory PostFactory --model=Post
public function definition(): array
{
    return [
        'title' => fake()->sentence(4),
        'slug' => fake()->unique()->slug(3),
        'excerpt' => fake()->paragraph(),
        'content' => '<p>'.fake()->paragraphs(3, true).'</p>',
        'status' => PostStatus::Draft,
        'published_at' => null,
    ];
}

State — حالت‌های از پیش تعریف‌شده

public function published(): static
{
    return $this->state(fn () => [
        'status' => PostStatus::Published,
        'published_at' => now()->subDays(rand(1, 30)),
    ]);
}

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

// استفاده
Post::factory()->published()->featured()->count(5)->create();

Relation در Factory

Post::factory()
    ->for(User::factory())
    ->for(Category::factory(), 'category')
    ->has(Tag::factory()->count(3))
    ->published()
    ->create();

for() belongsTo؛ has() hasMany/hasOne.

afterCreating — side effect کنترل‌شده

public function configure(): static
{
    return $this->afterCreating(function (Post $post) {
        $post->tags()->attach(Tag::factory()->count(2)->create());
    });
}

Seeder ترتیبی

class BlogSeeder extends Seeder
{
    public function run(): void
    {
        $user = User::factory()->create(['email' => 'author@test.local']);
        $categories = Category::factory()->count(3)->create();

        Post::factory()
            ->count(20)
            ->published()
            ->recycle($categories)
            ->for($user)
            ->create();
    }
}

recycle() از همان collection برای relation استفاده می‌کند — سریع‌تر.

withoutModelEvents در seed انبوه

Post::withoutEvents(fn () => Post::factory()->count(1000)->create());

از fire شدن Observer در seed جلوگیری می‌کند.

Seeder از فایل PHP (الگوی این پروژه)

داده در database/data/blog/posts.php — readable، version control، بدون Faker برای محتوای واقعی. ترکیب هر دو: Factory برای تست، PHP file برای محتوای seed ثابت.

اشتباهات

  • seed production با Faker
  • فراموش unique روی slug/email
  • ساخت ۱۰۰۰ رکورد با Observer سنگین
  • hardcode id به‌جای factory relation

جمع‌بندی

Factory = تست سریع. Seeder = محیط قابل تکرار. روابط، db:seed.

راه‌اندازی staging