迁移文件中不能写业务数据插入逻辑,因其职责仅为数据库结构变更;业务数据应通过Seeder填充,仅元数据可在迁移中用upsert等安全方式初始化。

迁移文件里不能写业务数据插入逻辑,php artisan migrate 执行的是结构变更,不是数据填充。真要往表里写数据,得用 Seeder(填充器),而不是在 up() 里硬塞 DB::insert() 或模型 create()。
为什么不能在 migration 的 up() 里写数据插入
迁移(Migration)的职责是定义数据库结构的“版本演进”:建表、加字段、改类型、删索引……它面向的是 schema,不是 data。Laravel 官方明确将数据写入归入 php artisan db:seed 流程,原因包括:
-
up()可能被反复执行(比如migrate:refresh),重复插入会报唯一键冲突或主键重复错误 - 迁移回滚(
migrate:rollback)只调用down(),而down()不会自动清理你在up()里插的数据,导致脏数据残留 - 团队协作时,别人运行
migrate期望只改结构;你混入数据逻辑,会破坏可预测性与幂等性 - 生产环境禁止手动执行填充操作,但迁移命令可能被 CI/CD 自动触发 —— 数据误插风险极高
正确做法:用 Seeder 分离结构与数据
Seeder 是 Laravel 专门设计用于初始化测试/默认数据的机制,和 Migration 解耦,支持单独运行、按需调用、可重复安全执行。
- 生成 Seeder:
php artisan make:seeder InitialUsersSeeder - 在
database/seeders/InitialUsersSeeder.php的run()方法里写插入逻辑,例如:DB::table('users')->insert([ ['name' => 'Admin', 'email' => 'admin@example.com', 'created_at' => now()], ]); - 运行填充:
php artisan db:seed --class=InitialUsersSeeder(开发阶段),或php artisan db:seed(配合DatabaseSeeder统一调度) - 若需在迁移后自动触发填充(极少见,仅限部署脚本),应在部署流程中显式调用
db:seed,而非塞进up()
特殊情况:migration 中必须写数据?那只能是元数据
极少数场景下,确实需要在迁移中写数据 —— 但仅限于“支撑结构存在所必需的元数据”,比如系统配置项、枚举状态码表、权限角色初始记录。这时必须满足:
- 数据是空表首次创建后的“最小可行初始化”,且不可变(如
status表里的active/inactive) - 使用
DB::table()->insertOrIgnore()或upsert()(Laravel 9+)避免重复失败 - 在
down()中对应删除这些行(不是删表),保证可逆 - 注释清楚“此为 schema 依赖型元数据,非业务数据”,并同步更新文档
例如在创建 statuses 表的迁移中插入基础状态:
Laravel Cloud(cloud.laravel.com)。此技能用于处理所有 Laravel Cloud 请求,包括搜索和读取数据。只要任务涉及 Laravel Cloud,即使用此技能。
DB::table('statuses')->upsert(
[['id' => 1, 'name' => 'active'], ['id' => 2, 'name' => 'inactive']],
['id'],
['name']
);
容易踩的坑:migration + model create() 混用
有人在 up() 里直接调用 User::create(...),这会触发 Eloquent 的全部生命周期(事件、访问器、强制转换、$fillable 检查),但此时表刚建好,模型可能还没加载,或时间戳字段未适配(如 $timestamps = false 未设),导致静默失败或字段丢失。更糟的是,如果模型启用了软删除或全局作用域,插入行为会偏离预期。
真正安全的方式只有两种:
- 纯 SQL 或 Query Builder(
DB::table()),绕过模型层,直操作表 - 交给 Seeder,用模型也更可控(可复用工厂、支持关系、易测)
别图省事把数据逻辑塞进 migration —— 后期排查数据不一致、回滚异常、CI 失败时,你会回来重读这一段。










