yii中无原生seeder,种子数据用migration+fake(初始化固定数据)或fixture(测试快照)实现;大数量、强逻辑、外部依赖场景应改用命令行controller或console command。

Yii 框架里没有原生的 Seeder 类,所谓“种子数据”实际是靠 Migration + Faker 手动拼出来的,或者用 Fixture 管理静态测试数据。选哪种,取决于你填的是「一次性初始化数据」还是「每次测试前重置的干净快照」。
Migration 里写种子逻辑:适合初始化 admin、配置项等固定数据
把填充代码塞进 migration 的 safeUp() 方法里,本质是把它当成“带业务逻辑的迁移”来用。它能被 ./yii migrate 触发,也受 migration 表记录控制,不会重复执行。
- 必须手动检查数据是否已存在,否则
up()多次运行会报主键冲突或唯一索引错误 ——SELECT COUNT(*) FROM user WHERE username = 'admin'是最轻量的判断方式 - 别直接 new Model 后调
save(),要加validate()或传false跳过验证(比如密码字段没传password_hash但 rules 里要求了) - Faker 实例得自己 new:
$faker = \Faker\Factory::create();,不能依赖yii2-faker的自动绑定,因为 Migration 不走应用组件生命周期 - 如果涉及多张表关联(如先插 role 再 assign permission),得按依赖顺序写 SQL 或用
createCommand()->insert(),避免模型 save() 触发未定义的事件或行为
示例片段:
public function safeUp($migration)
{
$faker = \Faker\Factory::create();
$count = (new \yii\db\Query())->from('user')->where(['username' => 'admin'])->count();
if ($count === 0) {
$user = new \app\models\User();
$user->username = 'admin';
$user->email = $faker->email();
$user->password_hash = \Yii::$app->security->generatePasswordHash('123456');
$user->save(false); // 跳过验证
}
}
Fixture 类填测试数据:适合单元/集成测试中反复加载+清理
Fixture 是 Yii 原生支持的测试数据管理机制,核心是 ActiveFixture 或 DbFixture,它会在测试开始前插入数据,结束后自动清空(或回滚事务)。
- 数据必须放在 PHP 数组文件里(如
@app/tests/_data/user.php),不能在 Fixture 类里动态生成 —— Faker 只能在 Migration 或自定义命令里用 -
ActiveFixture会自动调Model::insert(),但前提是模型的rules()允许该字段被批量赋值(safe场景),否则$model->attributes = $row会静默丢掉字段 - 别在
_fixtures()里写复杂逻辑,比如根据当前时间生成不同数据 —— Fixture 的设计目标是可重现,不是灵活性 - 多个 Fixture 有依赖时(如 user fixture 必须在 profile fixture 前加载),得在 Fixture 类里声明
public $depends = ['app\tests\fixtures\UserFixture'];,而不是靠文件名排序
什么时候不该用 Seeder/Fixture?
当你要填的数据量大(比如 10 万条日志)、字段逻辑复杂(如 status 根据 created_at 动态计算)、或需要和外部服务交互(如调 API 写 token)时,Migration 和 Fixture 都会卡住或不可靠。
- 大数量:改用命令行
Controller+batchInsert(),绕过 AR 层,直接拼 SQL - 强逻辑:写独立的 console
Command,继承yii\console\Controller,里面可以自由调服务、循环、sleep - 外部依赖:务必加 try/catch + rollback,并在命令末尾输出成功计数,别让失败静默吞掉
真正容易被忽略的是事务边界 —— Migration 默认开启事务,但 Fixture 在非事务模式下('transaction' => false)会直接删表再插,而有些数据库(如 MySQL MyISAM)不支持事务,这时数据就真没了。填之前,先确认你的测试数据库引擎和 Fixture 配置是否匹配。











