هنگام توسعه یک پروژه Laravel، خیلی زود با یک مشکل مهم مواجه میشویم: برای تست کردن برنامه به داده نیاز داریم.
فرض کنید در حال ساخت یک فروشگاه اینترنتی هستید. برای تست صفحه محصولات، جستوجو، Pagination، سبد خرید، سفارشها و پنل مدیریت، داشتن فقط دو یا سه محصول کافی نیست.
ممکن است بخواهید:
100 کاربر
500 محصول
1000 سفارش
3000 آیتم سفارش
در دیتابیس داشته باشید.
وارد کردن دستی این اطلاعات نهتنها زمانبر است، بلکه برای تستهای مختلف نیز انعطاف کافی ندارد.
Laravel برای حل این مسئله قابلیتی به نام Factory در اختیار ما قرار میدهد.
با Laravel Factory میتوانیم مشخص کنیم یک Model هنگام تولید داده آزمایشی چه اطلاعاتی داشته باشد و سپس به سادگی دهها، صدها یا هزاران رکورد ایجاد کنیم.
در این مقاله یاد میگیریم:
- Factory در Laravel چیست؟
- چرا از Factory استفاده میکنیم؟
- چگونه Factory بسازیم؟
- Factory چه ارتباطی با Model دارد؟
- Faker چیست؟
- چگونه دادههای تصادفی تولید کنیم؟
- چگونه یک رکورد بسازیم؟
- چگونه چند رکورد بسازیم؟
- چگونه Factory را با Seeder ترکیب کنیم؟
- چگونه دادههای مرتبط تولید کنیم؟
- چگونه State برای Factory ایجاد کنیم؟
- چگونه Factory را در تستها استفاده کنیم؟
- چه اشتباهاتی هنگام استفاده از Factory نباید انجام دهیم؟
# Factory در Laravel چیست؟
Factory در Laravel ابزاری برای تولید نمونههای دادهای مربوط به Modelهای برنامه است.
فرض کنید یک Model به نام Product داریم.
Factory میتواند مشخص کند وقتی میخواهیم یک محصول آزمایشی ایجاد کنیم، چه اطلاعاتی داشته باشد.
مثلاً:
Product::factory()->create();
Laravel بر اساس تعریف Factory میتواند یک محصول با اطلاعات نمونه در دیتابیس ایجاد کند.
یا:
Product::factory()->count(100)->create();
میتوانیم ۱۰۰ محصول ایجاد کنیم.
بنابراین Factory را میتوان ابزاری برای تولید سریع و قابل کنترل دادههای آزمایشی دانست.
# چرا به Factory نیاز داریم؟
فرض کنید میخواهیم صفحه محصولات یک فروشگاه را تست کنیم.
اگر فقط سه محصول داشته باشیم:
Laptop
Mouse
Keyboard
نمیتوانیم به خوبی مواردی مانند Pagination را بررسی کنیم.
اما با Factory:
Product::factory()->count(100)->create();
میتوانیم خیلی سریع ۱۰۰ محصول داشته باشیم.
حالا میتوانیم مواردی مانند:
- Pagination
- Search
- Filter
- Sort
- گزارشگیری
- داشبورد
- Performance
- Relationship
را بهتر آزمایش کنیم.
# Factory کجا قرار دارد؟
Factoryهای Laravel در مسیر زیر قرار میگیرند:
database/factories
مثلاً:
database/
└── factories/
├── UserFactory.php
├── ProductFactory.php
└── OrderFactory.php
هر Factory معمولاً به یک Model مرتبط است.
# Factory چگونه ساخته میشود؟
برای ساخت Factory از Artisan استفاده میکنیم.
مثلاً:
php artisan make:factory ProductFactory
Laravel فایل زیر را ایجاد میکند:
database/factories/ProductFactory.php
اما معمولاً بهتر است Factory را همراه Model مورد نظر ایجاد کنیم یا ساختار موجود Model را بررسی کنیم.
# Factory چه ارتباطی با Model دارد؟
فرض کنید Model زیر را داریم:
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
class Product extends Model
{
protected $fillable = [
'name',
'price',
];
}
Factory مربوط به آن میتواند در:
database/factories/ProductFactory.php
قرار داشته باشد.
برای استفاده:
Product::factory()
Laravel باید Factory مربوط به این Model را پیدا کند.
در ساختار استاندارد Laravel، Factory معمولاً با نام Model مرتبط است.
مثلاً:
Product
↓
ProductFactory
و:
User
↓
UserFactory
# ساخت اولین Factory
فرض کنیم جدول products دارای این ستونها است:
id
name
description
price
stock
is_active
created_at
updated_at
Factory میتواند به شکل زیر باشد:
<?php
namespace Database\Factories;
use Illuminate\Database\Eloquent\Factories\Factory;
class ProductFactory extends Factory
{
public function definition(): array
{
return [
'name' => fake()->words(3, true),
'description' => fake()->paragraph(),
'price' => fake()->randomFloat(2, 10000, 10000000),
'stock' => fake()->numberBetween(0, 100),
'is_active' => true,
];
}
}
متد:
definition()
مشخص میکند داده پیشفرض Factory چه باشد.
# Faker چیست؟
در Factory زیاد با چیزی مانند:
fake()
مواجه میشویم.
این تابع برای تولید دادههای نمونه و تصادفی استفاده میشود.
مثلاً:
fake()->name()
میتواند یک نام نمونه تولید کند.
یا:
fake()->email()
یک ایمیل نمونه ایجاد میکند.
یا:
fake()->sentence()
یک جمله نمونه تولید میکند.
این قابلیت برای تولید دادههای تستی بسیار کاربردی است.
# نمونههایی از دادههای Faker
نام
fake()->name()
ایمیل
fake()->email()
شماره تلفن
fake()->phoneNumber()
آدرس
fake()->address()
جمله
fake()->sentence()
پاراگراف
fake()->paragraph()
متن
fake()->text()
عدد تصادفی
fake()->numberBetween(1, 100)
عدد اعشاری
fake()->randomFloat(2, 10, 1000)
# ساخت یک رکورد با Factory
بعد از اینکه Factory آماده شد، میتوانیم از آن استفاده کنیم.
مثلاً:
Product::factory()->create();
این دستور یک رکورد Product در دیتابیس ایجاد میکند.
اگر بخواهیم رکورد را فقط در حافظه بسازیم و هنوز ذخیره نکنیم:
Product::factory()->make();
این تفاوت مهم است.
create
Product::factory()->create();
رکورد را در دیتابیس ذخیره میکند.
make
Product::factory()->make();
یک نمونه Model ایجاد میکند، اما آن را در دیتابیس ذخیره نمیکند.
# ساخت چند رکورد
برای ایجاد چند رکورد:
Product::factory()->count(10)->create();
یعنی:
10 Products
در دیتابیس ایجاد شود.
مثلاً:
Product::factory()
->count(100)
->create();
برای تولید ۱۰۰ محصول آزمایشی کاربرد دارد.
# استفاده از create بدون count
این کد:
Product::factory()->create();
یک رکورد ایجاد میکند.
در حالی که:
Product::factory()->count(50)->create();
۵۰ رکورد ایجاد میکند.
# Override کردن دادههای Factory
ممکن است Factory به صورت پیشفرض قیمت تصادفی ایجاد کند، اما برای یک تست خاص بخواهیم قیمت مشخصی داشته باشیم.
میتوانیم داده را Override کنیم:
Product::factory()->create([
'name' => 'MacBook Pro',
'price' => 50000000,
]);
در این حالت سایر مقادیر از Factory گرفته میشوند، اما name و price با مقادیر مشخصشده ایجاد میشوند.
# ساخت چند رکورد با مقدار مشخص
مثلاً:
Product::factory()
->count(10)
->create([
'is_active' => true,
]);
در این حالت همه محصولات ایجادشده is_active برابر true خواهند داشت.
# Factory و Seeder
یکی از مهمترین کاربردهای Factory ترکیب آن با Seeder است.
در مقاله قبلی دیدیم که Seeder برای وارد کردن داده به دیتابیس استفاده میشود.
حالا میتوانیم داخل DatabaseSeeder بنویسیم:
use App\Models\Product;
public function run(): void
{
Product::factory()
->count(100)
->create();
}
سپس:
php artisan db:seed
با اجرای Seeder، ۱۰۰ محصول ایجاد میشود.
# Factory و Seeder چه تفاوتی دارند؟
این دو ابزار مکمل یکدیگر هستند.
Factory
مشخص میکند:
> یک نمونه داده چگونه ساخته شود؟
Seeder
مشخص میکند:
> چه Seedهایی اجرا شوند و چه دادهای وارد محیط شود؟
مثلاً:
ProductFactory
↓
تعریف ساختار یک محصول تستی
DatabaseSeeder
↓
ProductFactory × 100
↓
100 Products
# Factory و Migration چه تفاوتی دارند؟
این سه مفهوم را بهتر است از یکدیگر جدا کنیم:
| ابزار | وظیفه |
|---|---|
| Migration | ساختار دیتابیس |
| Seeder | داده اولیه |
| Factory | داده نمونه و آزمایشی |
| Eloquent | کار با دادهها |
مثلاً:
Migration
↓
products table
Factory
↓
product test data
Seeder
↓
run factory
Eloquent
↓
read/update/delete products
# ساخت دادههای مرتبط
در پروژههای واقعی دادهها معمولاً مستقل نیستند.
مثلاً:
User
↓
Order
↓
OrderItem
↓
Product
برای تست چنین سیستمی باید بتوانیم دادههای مرتبط ایجاد کنیم.
Factory این امکان را فراهم میکند.
# ایجاد User همراه با Product
فرض کنیم محصول متعلق به یک User است:
Product::factory()->create([
'user_id' => User::factory()->create()->id,
]);
ابتدا User ایجاد میشود و سپس Product با user_id آن ساخته میشود.
البته در پروژههای واقعی، بهتر است Factoryها و Relationshipها را طوری طراحی کنیم که این روابط خواناتر و قابل استفاده مجدد باشند.
# استفاده از Relationship در Factory
فرض کنید Model Product یک رابطه با User دارد.
میتوانیم در Factory از Factory مربوط به User استفاده کنیم:
'user_id' => User::factory(),
مثلاً:
return [
'user_id' => User::factory(),
'name' => fake()->words(3, true),
'price' => fake()->randomFloat(2, 10000, 1000000),
];
با ایجاد Product، User مورد نیاز نیز میتواند به صورت خودکار ایجاد شود.
# ساخت داده با Relationship
یکی از قابلیتهای قدرتمند Factory این است که میتوانیم دادههای مرتبط تولید کنیم.
مثلاً فرض کنیم هر Category چند Product دارد.
میتوانیم چیزی شبیه این داشته باشیم:
$category = Category::factory()->create();
Product::factory()
->count(20)
->create([
'category_id' => $category->id,
]);
نتیجه:
Category
│
├── Product
├── Product
├── Product
└── ...
این روش برای تست Relationshipها بسیار مفید است.
# Factory State چیست؟
گاهی یک Model حالتهای مختلف دارد.
مثلاً Product:
Active
Inactive
Out of Stock
Featured
یا Order:
Pending
Paid
Shipped
Completed
Cancelled
به جای اینکه برای هر حالت Factory جدا بسازیم، میتوانیم از State استفاده کنیم.
مثلاً:
public function inactive(): static
{
return $this->state(fn (array $attributes) => [
'is_active' => false,
]);
}
حالا:
Product::factory()
->inactive()
->create();
یک محصول غیرفعال ایجاد میکند.
# ساخت State برای Product
یک Factory میتواند مثلاً اینگونه باشد:
class ProductFactory extends Factory
{
public function definition(): array
{
return [
'name' => fake()->words(3, true),
'price' => fake()->randomFloat(2, 10000, 1000000),
'stock' => fake()->numberBetween(0, 100),
'is_active' => true,
];
}
public function inactive(): static
{
return $this->state(fn (array $attributes) => [
'is_active' => false,
]);
}
}
حالا:
Product::factory()
->inactive()
->create();
# چند State مختلف
میتوانیم Stateهای مختلف ایجاد کنیم:
public function active(): static
{
return $this->state(fn (array $attributes) => [
'is_active' => true,
]);
}
public function inactive(): static
{
return $this->state(fn (array $attributes) => [
'is_active' => false,
]);
}
حالا:
Product::factory()->active()->create();
یا:
Product::factory()->inactive()->create();
این روش باعث میشود Factory خواناتر و قابل استفاده مجددتر شود.
# استفاده از Factory برای تست Pagination
یکی از مثالهای ساده و کاربردی:
Product::factory()
->count(100)
->create();
حالا اگر صفحه محصولات شما دارای:
10 products per page
باشد، میتوانید Pagination را با داده واقعیتر آزمایش کنید.
# استفاده از Factory برای تست Search
فرض کنید میخواهیم جستوجوی محصولات را تست کنیم.
میتوانیم چند محصول ایجاد کنیم:
Product::factory()->create([
'name' => 'Laravel Course',
]);
Product::factory()->create([
'name' => 'PHP Course',
]);
Product::factory()->create([
'name' => 'WordPress Course',
]);
سپس عملکرد Search را آزمایش کنیم.
# استفاده از Factory در Feature Test
Factory در تستهای Laravel اهمیت زیادی دارد.
مثلاً:
$user = User::factory()->create();
سپس میتوانیم با همان User یک درخواست تستی ایجاد کنیم:
$this->actingAs($user)
->get('/dashboard')
->assertOk();
در اینجا Factory باعث میشود بدون ایجاد دستی کاربر، داده مورد نیاز تست را ایجاد کنیم.
# استفاده از Factory در Unit و Feature Test
در تستها ممکن است به دادههای مختلفی نیاز داشته باشیم:
User
Product
Order
Category
Permission
به جای اینکه هر تست اطلاعات را دستی ایجاد کند، Factory میتواند این فرآیند را ساده کند.
مثلاً:
$user = User::factory()->create();
$product = Product::factory()->create();
$order = Order::factory()->create([
'user_id' => $user->id,
]);
این رویکرد تستها را خواناتر میکند.
# Factory و Faker چه تفاوتی دارند؟
Faker و Factory یکی نیستند.
Faker ابزار تولید مقادیر نمونه است.
مثلاً:
fake()->name()
یک نام نمونه تولید میکند.
Factory ساختار ایجاد Model را تعریف میکند.
مثلاً:
User::factory()->create();
پس:
Faker
↓
Sample Values
Factory
↓
Sample Models
Factory میتواند از Faker برای تولید مقادیر استفاده کند.
# داده فارسی در Factory
اگر پروژه شما فارسی است، ممکن است بخواهید دادههای فارسی ایجاد کنید.
برای مثال، میتوانید به جای اینکه همه چیز را از Faker بگیرید، مجموعهای از مقادیر مشخص تعریف کنید:
'name' => fake()->randomElement([
'لپ تاپ',
'موبایل',
'مانیتور',
'کیبورد',
'ماوس',
]),
یا برای وضعیت:
'status' => fake()->randomElement([
'pending',
'paid',
'completed',
]);
این روش برای دادههایی که مجموعه مقادیر مشخص دارند بسیار کاربردی است.
# تولید داده تصادفی با randomElement
مثلاً:
'category' => fake()->randomElement([
'Laravel',
'PHP',
'WordPress',
'n8n',
]),
هر بار یکی از مقادیر انتخاب میشود.
# Factory برای تولید دادههای واقعیتر
هرچه دادههای تستی به شرایط واقعی نزدیکتر باشند، تست سیستم ارزشمندتر میشود.
مثلاً برای محصول فقط:
'name' => 'Product',
قرار ندهیم.
بلکه:
'name' => fake()->words(3, true),
'description' => fake()->paragraph(),
'price' => fake()->randomFloat(2, 10000, 10000000),
'stock' => fake()->numberBetween(0, 100),
ایجاد کنیم.
این کار کمک میکند مشکلاتی که فقط با دادههای متنوع دیده میشوند، زودتر پیدا شوند.
# Factory و دادههای تکراری
یکی از مشکلات رایج این است که Factory برای ستونی که باید Unique باشد، داده تصادفی غیر یکتا تولید کند.
مثلاً:
'email' => fake()->email(),
در بیشتر مواقع برای دادههای نمونه مناسب است، اما اگر منطق تست به Unique بودن حساس باشد باید سناریو را آگاهانه طراحی کنیم.
مثلاً برای ایجاد ایمیل مشخص:
User::factory()->create([
'email' => 'test@example.com',
]);
یا در تست، دادههای Unique مورد نیاز را صریحاً تعیین کنیم.
# Factory و Hash کردن Password
در Factory کاربر معمولاً نیاز به Password دارد.
یک نمونه رایج:
'password' => Hash::make('password'),
اما اگر Factory استاندارد پروژه از مقدار Hashشده ثابت استفاده کند، بهتر است از همان ساختار پیشفرض Laravel استفاده شود و بیدلیل Password را در هر بار ایجاد رکورد دوباره Hash نکنیم.
در تستها نیز باید توجه کنیم که Password واقعی و حساس وارد دادههای تستی نشود.
# Factory در محیط Development
یکی از بهترین کاربردهای Factory، آمادهسازی سریع محیط توسعه است.
مثلاً:
User::factory()->count(100)->create();
Category::factory()->count(20)->create();
Product::factory()->count(500)->create();
با چند دستور میتوان یک دیتابیس نسبتاً بزرگ برای توسعه و تست داشت.
# Factory در محیط Production
Factory معمولاً برای تولید داده آزمایشی است.
بنابراین نباید بدون دلیل آن را روی Production اجرا کنیم.
مثلاً:
User::factory()->count(10000)->create();
در محیط Production میتواند هزاران رکورد ناخواسته ایجاد کند.
به همین دلیل Factory و دادههای تستی باید از فرآیندهای واقعی Production جدا نگه داشته شوند.
# Factory و Seeder در یک پروژه واقعی
یک ساختار مناسب میتواند چنین چیزی باشد:
database/
├── factories/
│ ├── UserFactory.php
│ ├── ProductFactory.php
│ └── OrderFactory.php
│
└── seeders/
├── DatabaseSeeder.php
├── RoleSeeder.php
└── DevelopmentSeeder.php
سپس Seeder میتواند Factoryها را اجرا کند.
مثلاً:
public function run(): void
{
User::factory()->count(50)->create();
Product::factory()->count(200)->create();
Order::factory()->count(100)->create();
}
# یک مثال کامل؛ ساخت دیتای فروشگاه
فرض کنیم میخواهیم یک فروشگاه برای تست داشته باشیم.
ابتدا کاربران:
User::factory()
->count(20)
->create();
دستهبندیها:
Category::factory()
->count(10)
->create();
محصولات:
Product::factory()
->count(100)
->create();
حالا میتوانیم اطلاعات بیشتری برای تست داشته باشیم.
در پروژه واقعی بهتر است Relationshipها نیز به شکل اصولی در Factoryها طراحی شوند تا بتوانیم دادههای وابسته را با کمترین کد ایجاد کنیم.
# اجرای Factory از Tinker
برای آزمایش سریع Factory میتوانیم از Tinker استفاده کنیم:
php artisan tinker
سپس:
Product::factory()->create();
یا:
Product::factory()->count(10)->create();
بعد میتوانیم دیتابیس را بررسی کنیم.
Tinker برای آزمایش سریع Model و Factory بسیار کاربردی است.
# Factory و پاک کردن دادههای تستی
اگر دادههای زیادی با Factory ایجاد کردهایم، در محیط Development میتوانیم از روشهای مناسب برای بازسازی دیتابیس استفاده کنیم.
مثلاً:
php artisan migrate:fresh --seed
اما باید توجه کنیم که این دستور تمام جدولهای دیتابیس را حذف میکند.
بنابراین استفاده از آن برای Production مناسب نیست.
# اشتباهات رایج در استفاده از Factory
۱. استفاده از Factory برای دادههای واقعی
Factory عمدتاً برای دادههای نمونه و آزمایشی طراحی شده است.
دادههای واقعی Production نباید با Factory تولید شوند.
۲. ایجاد بیش از حد داده
این کد:
User::factory()->count(1000000)->create();
میتواند منابع قابل توجهی مصرف کند.
تعداد داده باید متناسب با هدف تست باشد.
۳. نادیده گرفتن Relationshipها
اگر Product به Category وابسته است، Factory باید این وابستگی را به شکل درستی مدیریت کند.
۴. ایجاد داده نامعتبر
اگر قیمت محصول باید همیشه مثبت باشد، Factory نباید مقادیر نامعتبر ایجاد کند.
مثلاً:
'price' => fake()->randomFloat(2, 10000, 1000000),
از ایجاد قیمت منفی جلوگیری میکند.
۵. تولید دادههای غیرواقعی
داده تستی خیلی ساده ممکن است مشکلات واقعی UI یا Query را نشان ندهد.
هرچه سناریوی تست به داده واقعی نزدیکتر باشد، ارزش آن بیشتر است.
# بهترین روش طراحی Factory
برای یک Factory خوب بهتر است:
۱. دادههای پیشفرض منطقی داشته باشد
'name' => fake()->words(3, true),
۲. Stateهای پرکاربرد تعریف کند
مثلاً:
active()
inactive()
۳. Relationshipها را در نظر بگیرد
مثلاً:
'user_id' => User::factory(),
۴. از دادههای غیرممکن جلوگیری کند
مثلاً موجودی منفی ایجاد نکنیم.
۵. Factory را بیش از حد پیچیده نکنیم
Factory باید ابزار تولید داده باشد، نه محلی برای قرار دادن حجم زیادی از منطق تجاری برنامه.
# Factory، Seeder و Eloquent در کنار هم
تا اینجا مسیر دیتابیس Laravel ما تقریباً به این شکل شده است:
Migration
↓
ساختار دیتابیس
Seeder
↓
داده اولیه
Factory
↓
داده تستی
Eloquent
↓
کار با دادهها
این چهار مفهوم پایه مهمی برای کار حرفهای با دیتابیس در Laravel هستند.
# جمعبندی
Factory یکی از ابزارهای بسیار کاربردی Laravel برای تولید دادههای نمونه و آزمایشی است.
با Factory میتوانیم بدون وارد کردن دستی اطلاعات، تعداد زیادی Model ایجاد کنیم.
مثلاً:
Product::factory()->create();
برای یک رکورد و:
Product::factory()
->count(100)
->create();
برای ایجاد ۱۰۰ رکورد استفاده میشود.
همچنین میتوان Factory را با:
- Faker
- Seeder
- Eloquent
- Relationship
- Feature Test
- Tinker
ترکیب کرد.
مهمترین نکته این است که Factory را صرفاً ابزاری برای تولید چند رکورد تصادفی نبینیم؛ در پروژههای حرفهای، Factory میتواند پایه مهمی برای تست، توسعه، ساخت داده نمونه و آمادهسازی محیط Development باشد.
# سوالات متداول درباره Factory در Laravel
Factory در Laravel چیست؟
Factory ابزاری برای تعریف و تولید نمونههای داده مربوط به Modelهای Laravel است.
چگونه Factory بسازیم؟
با Artisan:
php artisan make:factory ProductFactory
چگونه با Factory یک رکورد بسازیم؟
Product::factory()->create();
چگونه چند رکورد بسازیم؟
Product::factory()->count(100)->create();
تفاوت create و make در Factory چیست؟
create() رکورد را در دیتابیس ذخیره میکند، اما make() یک نمونه Model میسازد بدون اینکه آن را در دیتابیس ذخیره کند.
Faker در Laravel چیست؟
Faker برای تولید مقادیر نمونه مانند نام، ایمیل، آدرس، متن و اعداد تصادفی استفاده میشود.
آیا Factory با Seeder قابل استفاده است؟
بله. یکی از کاربردهای رایج این است که Seeder، Factory را برای تولید دادههای تستی اجرا کند.
آیا Factory برای Production مناسب است؟
Factory عمدتاً برای دادههای آزمایشی و Development استفاده میشود و نباید بدون دلیل در Production اجرا شود.
# مقاله بعدی در مسیر Laravel
تا اینجا مسیر دیتابیس در Cluster به این شکل است:
Migration
↓
Seeder
↓
Factory
↓
Eloquent
↓
Relationship
بنابراین مقاله بعدی طبق برنامه، Eloquent در Laravel است؛ جایی که وارد بخش بسیار مهم کار با دیتابیس و Modelها در Laravel میشویم.





