Laravel生产环境初始数据填充:迁移与种子器的选型及实现方案咨询
最近做了些研究后,我卡在了一个问题上:Laravel项目上线时,默认/初始数据该用种子器(Seeder)还是迁移(Migration)来填充?目前我看到两种完全不同的思路,想和大家聊聊我的困惑,还有自己的一些实践疑问。
支持用迁移填充初始数据的观点
有不少开发者坚定站在迁移这边,比如Nuno Maduro就明确说:
种子器应该只用于环境特定的数据(本地或测试环境),不能用来存放系统默认值。数据库种子器会随着时间过时,可能无法反映生产数据的真实状态。如果需要重新运行迁移在其他环境部署应用,这会引入不一致性。相比之下,迁移天生可靠,因为它们定义了塑造生产数据的确切结构和转换逻辑,能保证跨环境的一致性。
还有另一位开发者补充了几个核心理由:
- 支持表之间的依赖关系
- 可以通过
down()方法回滚版本,顺便移除插入的数据,更方便回滚发布版本 - 避免有人忘记运行种子器,或是不小心在生产环境运行了测试用的种子器
支持用种子器的观点
另一方面,很多开发者的主张是:
迁移应该只负责迁移数据库结构,而不是填充数据。
不过这个观点往往没提到:默认数据其实也是应用架构的一部分。
我项目里的两种默认数据场景
我的项目里有两类不同的默认数据,我现在的取舍和疑问如下:
- 角色数据(user、admin):这是系统的核心关键数据,我目前倾向于用迁移来插入它们。
- 菜品分类与子分类(DishCategories & DishSubcategories):这里有点纠结。我有一个很大的预定义分类/子分类数组(相当于初始数据集),为了不让迁移文件太臃肿,我把这个数组移到了单独的PHP文件里,在插入数据时引入它。想问问大家,这种做法可行吗?
- 一方面,这些不是“测试数据”,是应用逻辑的一部分(比如用于搜索功能);
- 另一方面,后续这些分类可以通过后台管理面板来修改更新。
遇到的潜在问题
另外,Laravel Daily也提到过把数据填充放在迁移里的一个隐患:
当运行自动化测试时,如果重新执行迁移,默认数据可能会被多次插入,除非用
insertOrIgnore这类方法做防护,否则测试环境里会出现重复行。
还有一种方案是用自定义Artisan命令来填充数据,但我觉得它和种子器逻辑类似,而且需要在迁移执行完后手动/自动运行这个命令,所以暂时没做这个功能。
两种方案的代码示例
我整理了种子器和迁移两种方案的代码,供大家参考:
种子器方案
class RolesSeeder extends Seeder { public function run(): void { Role::create(['name' => 'user']); Role::create(['name' => 'admin']); } }
迁移方案
public function up(): void { Schema::create('roles', function (Blueprint $table) { $table->id(); $table->string('name')->unique(); $table->timestamps(); }); $roles = [ 'user', 'admin' ]; foreach ($roles as $role){ DB::table('roles')->insert([ 'name' => $role, 'created_at' => now(), 'updated_at' => now(), ]); } }
我知道之前Stack Exchange上有过类似的问题《Laravel : Migrations & Seeding for production data》,但那个问题比较久了,所以还是想重新发起这个讨论,看看大家现在的实践和建议。
内容来源于stack exchange

