
在 Laravel 8 迁移中,不能在 Schema::create() 回调内创建模型实例并期望自动写入数据库;必须在表结构创建完成后,通过独立语句执行数据插入,并确保模型启用批量赋值保护($fillable)或使用显式赋值+save()。
在 laravel 8 迁移中,不能在 `schema::create()` 回调内创建模型实例并期望自动写入数据库;必须在表结构创建完成后,通过独立语句执行数据插入,并确保模型启用批量赋值保护(`$fillable`)或使用显式赋值+`save()`。
在 Laravel 应用中,数据库迁移(Migrations)的核心职责是定义和变更数据库结构(如创建表、添加字段、修改索引),而非填充业务数据。因此,像“创建模板表后立即插入三条默认模板”这类需求,需严格分离结构定义与数据初始化逻辑——即:先建表,再插数据。
以下是在 Laravel 8 中安全、规范地实现该目标的完整方案:
✅ 正确做法:迁移中分两步执行
- 定义表结构(在 Schema::create 内完成)
- 插入初始数据(在 Schema::create 之后、同一迁移文件中执行)
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
use App\Models\Template; // 确保引入模型类
return new class extends Migration
{
public function up(MigrationBuilder $migration): void
{
// 步骤 1:创建 templates 表
Schema::create('templates', function (Blueprint $table) {
$table->id();
$table->string('name')->unique();
$table->timestamps();
});
// 步骤 2:插入默认模板数据(关键!必须放在 create 之后)
Template::insert([
['name' => 'Template 1', 'created_at' => now(), 'updated_at' => now()],
['name' => 'Template 2', 'created_at' => now(), 'updated_at' => now()],
['name' => 'Template 3', 'created_at' => now(), 'updated_at' => now()],
]);
}
public function down(MigrationBuilder $migration): void
{
Schema::dropIfExists('templates');
}
};? 推荐使用 insert() 而非 create() 或 save():
- insert() 是原生 SQL 批量插入,性能高、无模型事件触发、不校验 $fillable,适合初始化静态数据;
- 若需触发模型事件(如 creating/created)或依赖 Eloquent 验证逻辑,则改用 create(),但必须提前配置 $fillable:
// 在 app/Models/Template.php 中 protected $fillable = ['name'];
随后可使用更简洁的集合式写法(需引入 Illuminate\Support\Collection):
use Illuminate\Support\Collection; // … 在 up() 方法中,Schema::create 之后: Collection::times(3, fn ($i) => Template::create(['name' => "Template " . ($i + 1)]));
⚠️ 常见错误与注意事项
- ❌ 禁止在 Schema::create 回调中操作模型:此时表尚未存在,Eloquent 会抛出 SQLSTATE[42S02] 错误;
- ❌ 未声明 $fillable 却使用 create():将触发 MassAssignmentException,因 Laravel 默认拒绝批量赋值以保障安全;
- ⚠️ 时间戳字段需显式传入:若使用 insert(),created_at/updated_at 不会自动填充,必须手动指定(如 now());
- ? 复杂初始化建议抽离至 Seeder:若需关联数据、条件逻辑或大量记录,应使用 Database Seeder,并在迁移后通过 php artisan db:seed --class=TemplateSeeder 触发;
- ? 回滚兼容性:down() 方法中仅需删除表,无需“删除初始数据”,因其属于表结构的一部分生命周期。
✅ 总结
迁移的本质是架构演进工具,不是数据填充脚本。初始化数据应作为迁移的“副作用”在结构就绪后执行,优先选用 insert() 保证效率与稳定性;同时务必检查模型的 $fillable 配置,并在生产环境中验证迁移幂等性(多次 migrate:fresh 不应报错或重复插入)。遵循此模式,既能保障数据库一致性,又符合 Laravel 的设计哲学与最佳实践。










