本文目录导读:

在PHP项目中实现“数据工厂”(Data Factory),通常是为了批量生成测试数据或快速构建业务对象,这在单元测试、功能测试以及开发阶段模拟真实数据时非常有用。
最经典的标准源于Laravel的 ModelFactory,但它的核心思想可以应用于任何PHP项目,下面分别介绍在Laravel框架中和在原生PHP(或非Laravel框架)中的实现方式。
核心概念
一个数据工厂(Factory)本质上是一个对象构造器,它通常包含:
- 属性生成器: 针对对象的每个字段,定义如何随机生成值(如使用 Faker 库)。
- 状态(States): 支持对某些字段进行特定的预设覆盖(如生成“已支付”订单)。
- 序列(Sequence): 在生成多个数据时,定义字段的循环变化。
在 Laravel 框架中(最标准、最推荐)
Laravel 自带了一套极其强大的 Eloquent 模型工厂系统。
定义 Factory
使用 artisan make:factory 或直接在 database/factories/ 下创建类。
<?php
// database/factories/UserFactory.php
namespace Database\Factories;
use App\Models\User;
use Illuminate\Database\Eloquent\Factories\Factory;
use Illuminate\Support\Str;
class UserFactory extends Factory
{
protected $model = User::class;
public function definition()
{
return [
'name' => fake()->name(), // 使用 Faker 生成随机姓名
'email' => fake()->unique()->safeEmail(),
'email_verified_at' => now(),
'password' => '$2y$10$92IXUNpkjO0rOQ5byMi.Ye4oKoEa3Ro9llC/.og/at2.uheWG/igi', // password
'remember_token' => Str::random(10),
];
}
// 定义状态 (States)
public function unverified()
{
return $this->state(function (array $attributes) {
return [
'email_verified_at' => null,
];
});
}
// 定义序列 (Sequences)
public function adminSequence()
{
return $this->sequence(function ($sequence) {
return ['role' => $sequence->index === 0 ? 'admin' : 'user'];
});
}
}
使用 Factory 生成数据
在你的测试(phpunit.xml 中的 Tests\Feature 或 Tests\Unit)或 Tinker 中使用:
// 1. 创建一个用户
$user = User::factory()->create();
// 2. 创建一个未验证邮箱的用户(使用状态)
$unverifiedUser = User::factory()->unverified()->create();
// 3. 创建 50 个用户
$users = User::factory()->count(50)->create();
// 4. 创建时覆盖特定字段
$specificUser = User::factory()->create([
'name' => '张三',
'email' => 'zhangsan@example.com',
]);
// 5. 使用序列
$users = User::factory()->count(3)->adminSequence()->create();
// 结果:第1个 role = 'admin', 第2、3个 role = 'user'
// 6. 创建关联数据(如果需要)
$post = \App\Models\Post::factory()
->for($user) // 关联到已存在的用户
->count(10)
->create();
优点: 与 Laravel 模型、关联、数据库迁移深度集成,支持 make(仅创建对象不保存)和 create(保存到数据库)。
在原生 PHP(或非 Laravel 项目)中实现
你需要引入 faker 库,并自己管理模型实例的创建。
安装依赖(Composer)
composer require fakerphp/faker
定义一个基础 Factory 类
创建一个基础 Factory 类,用于管理模型实例化和属性生成。
<?php
// src/Core/Factory.php
namespace App\Core;
use Faker\Factory as Faker;
abstract class Factory
{
protected $faker;
protected $model; // 需要子类指定模型类名
public function __construct()
{
$this->faker = Faker::create();
}
// 子类必须实现此方法,返回关联数组
abstract public function definition(): array;
// 创建一个实例(不保存)
public function make(array $overrides = []): object
{
$attributes = array_merge($this->definition(), $overrides);
$modelClass = $this->model;
return new $modelClass($attributes);
}
// 创建并保存(假设模型有 save 方法或使用 PDO)
public function create(array $overrides = []): object
{
$instance = $this->make($overrides);
$instance->save(); // 伪代码,实际应调用数据库插入逻辑
return $instance;
}
// 批量创建
public function count(int $number): self
{
$this->count = $number;
return $this;
}
// 带状态的创建
public function state(array $stateAttributes): self
{
$this->state = $stateAttributes;
return $this;
}
}
定义具体实体的 Factory(如 UserFactory)
<?php
// src/Database/Factories/UserFactory.php
namespace App\Database\Factories;
use App\Core\Factory;
class UserFactory extends Factory
{
protected $model = \App\Models\User::class;
public function definition(): array
{
return [
'name' => $this->faker->name(),
'email' => $this->faker->unique()->safeEmail(),
'password' => password_hash('password', PASSWORD_DEFAULT),
'is_admin' => false,
'created_at' => $this->faker->dateTimeThisYear(),
];
}
// 自定义状态方法(在子类中实现)
public function admin(): self
{
return $this->state(['is_admin' => true]);
}
}
使用
// 创建单个用户
$userFactory = new \App\Database\Factories\UserFactory();
$user = $userFactory->create(['name' => '测试用户']);
// 批量创建10个管理员
$adminUsers = [];
for ($i = 0; $i < 10; $i++) {
$adminUsers[] = $userFactory->admin()->create();
}
注意: 你需要自己实现 save() 逻辑(使用 PDO、Eloquent 或 Doctrine),如果项目使用 Eloquent(例如基于 Laravel 的包或独立组件),可以直接使用 Laravel 的方式。
使用独立的 PHP 包(推荐复杂项目)
如果有更复杂的需求(如关联、序列、状态机),建议直接使用成熟的库:
fakerphp/faker(核心)zenstruck/foundry(适用于 Symfony 项目,非常强大)bavix/laravel-wallet-factory(Pest PHP 的工厂支持)
或者,如果你希望完全独立于框架,可以构建一个带有 Fluent 接口的工厂类,参考 Laravel 的设计思路。
核心最佳实践
| 场景 | 建议方案 |
|---|---|
| Laravel 项目 | 直接使用内置的 Model Factory(HasFactory trait) |
| Symfony 项目 | zenstruck/foundry 或 Alice 配合 Faker |
| 小型原生项目 | 手动实现一个简单的 Factory 类 + fakerphp/faker |
| 需要复杂关联 | 用 Laravel 模式(它处理关联最优雅),或使用 Dao 层封装 |
| 仅需生成随机数据 | 只使用 fakerphp/faker 本身即可 |
- 最佳实践(Laravel): 使用
HasFactorytrait 和database/factories/下的类。 - 通用方案: 结合
fakerphp/faker+ 自己的Factory抽象类 + 你的Model类。 - 核心代码: 数据工厂的循环本质是
definition()方法 +state()覆盖 +count()批量生成。
如果你能说明你的项目是否使用特定框架,我可以给出更具体的代码示例。