Laravel 的模型工厂(Eloquent Factories)是工厂模式在现代 PHP 测试与种子数据场景下的精妙演进,它融合了工厂方法(Factory Method)、生成器(Builder)和流式接口(Fluent Interface),并通过FactoryBuilder实现了强大的链式配置与延迟实例化。
一、User::factory()->create()使用的是哪种工厂模式?
✅ 核心:工厂方法模式(Factory Method) + 生成器模式(Builder)的混合体
| 组成部分 | 模式角色 | 说明 |
|---|---|---|
User::factory() | 工厂方法(Factory Method) | 在 Eloquent 模型中(通过HasFactorytrait),factory()是一个静态工厂方法,返回FactoryBuilder实例 |
->count(3)->for($team)->create() | 生成器模式(Builder) | 通过链式调用累积配置(数量、关联、状态等),最后create()触发实际对象构建 |
Database\Factories\UserFactory | 具体工厂(Concrete Factory) | 定义definition()方法,描述如何生成单个模型实例的属性 |
📌这不是“简单工厂”(一个类生成多种产品),
也不是“抽象工厂”(生成产品族),
而是“可配置的工厂方法 + 延迟执行的构建器”。
二、FactoryBuilder如何实现链式调用?
FactoryBuilder的核心设计是状态累积 + 流式接口。关键机制如下:
1.内部状态存储
classFactoryBuilder{protected$factory;// 实际的 UserFactory 实例protected$count=1;// 要创建的数量protected$states=[];// 应用的状态(如 'admin')protected$has=[];// 关联关系(如 for($team))protected$for=null;// 父级关联}2.链式方法返回$this
publicfunctioncount(int$count){$this->count=$count;return$this;// 实现链式}publicfunctionfor(Model$model,?string$relationship=null){$this->for=compact('model','relationship');return$this;}publicfunctionstate(string$state){$this->states[]=$state;return$this;}✅ 这是Fluent Interface(流式接口)的标准实现:每个配置方法返回
$this,允许连续调用。
三、FactoryBuilder如何实现“延迟创建”?
关键在于:直到调用create()(或make())时,才真正执行模型实例化。在此之前,所有操作只是记录意图。
create()的执行流程:
publicfunctioncreate(array$attributes=[]){// 1. 循环 $this->count 次foreach(range(1,$this->count)as$_){// 2. 调用实际工厂的 definition()$definition=$this->factory->definition();// 3. 合并额外属性$attrs=array_merge($definition,$attributes);// 4. 应用 states(如 'admin' 状态会覆盖某些字段)foreach($this->statesas$state){$attrs=$this->factory->applyState($state,$attrs);}// 5. 处理关联(如 for($team))if($this->for){$attrs[$this->for['relationship']??'team_id']=$this->for['model']->id;}// 6. 创建并保存模型$model=$this->factory->newModel($attrs);$model->save();$results[]=$model;}return$this->count===1?$results[0]:$results;}🔑延迟创建的价值:
- 允许在
create()前动态组合条件;- 避免在
factory()调用时就执行数据库操作;- 支持
make()(不保存)和create()(保存)两种行为。
四、背后的模式协同:为什么这样设计?
| 设计目标 | 实现方式 | 对应模式 |
|---|---|---|
| 创建可配置的测试数据 | count(),state(),for()累积配置 | Builder 模式 |
| 解耦模型与创建逻辑 | UserFactory独立定义definition() | 工厂方法 |
| 支持链式表达 | 所有配置方法返回$this | Fluent Interface |
| 避免过早实例化 | 直到create()才构建 | 延迟求值(Lazy Evaluation) |
| 支持复杂关联 | for(),has()记录关系上下文 | 上下文构建器(Contextual Builder) |
五、与你关心的工程原则高度一致
- 可测试性:工厂独立于模型,可为不同测试场景生成不同数据;
- 可维护性:
UserFactory集中管理用户数据生成逻辑; - 组合优于继承:通过
state('admin')组合行为,而非继承AdminUserFactory; - 避免全局状态:每次
factory()返回新FactoryBuilder,无副作用; - 接口清晰:
definition()是唯一必须实现的抽象,符合 ISP。
六、对比传统工厂模式
| 传统工厂方法 | Laravel Eloquent Factory |
|---|---|
UserFactory::createAdmin() | User::factory()->state('admin')->create() |
| 静态方法,行为固定 | 动态组合,行为灵活 |
| 每种变体需一个方法 | 通过state/count/for自由组合 |
| 立即创建 | 延迟创建,支持链式配置 |
✅ Laravel 的工厂是对工厂模式的现代化、动态化、组合化演进,完美适配测试与种子数据场景。
结语
User::factory()->create()的优雅,源于工厂方法提供入口,生成器模式提供组合能力,流式接口提供表达力。FactoryBuilder作为“意图记录器”和“执行调度器”,实现了配置与创建的分离,这正是所重视的“关注点分离”与“可组合性”的典范。
它不是简单套用 GoF 模式,而是根据 PHP 动态特性和测试需求,对模式进行的创造性融合——这正是 Laravel 设计哲学的精髓。