Laravel软删除迁移单元测试失败问题求助
Laravel软删除迁移导致单元测试失败的解决方案
问题背景
为现有Inclusion模型添加软删除功能后,本地及预发布环境运行正常,但单元测试(每次重建数据库并执行所有迁移)报错,提示找不到deleted_at列。原因在于:
- 旧迁移中使用Eloquent查询
inclusions表时,模型已启用软删除特性,会自动拼接WHERE deleted_at IS NULL条件; - 软删除的迁移(添加
deleted_at列)执行顺序在旧迁移之后,导致旧迁移执行时数据库中尚未存在该列,SQL查询触发报错。
可行解决方案
方案1:修改旧迁移,临时关闭软删除作用域
针对那个同步inclusion_vertical数据的旧迁移,将Eloquent查询替换为不带软删除全局作用域的版本,避免查询时引用不存在的deleted_at列:
// 替换原有的 Inclusion::each(...) Inclusion::withoutGlobalScopes(['Illuminate\Database\Eloquent\SoftDeletingScope'])->each(function (Inclusion $inclusion) use ($verticals) { $verticalIds = json_decode($inclusion->getAttribute('verticals')); $inclusion->verticals()->sync($verticals->whereIn('external_id', $verticalIds)); });
更稳妥的方式是直接使用数据库查询构建器,完全绕开模型的软删除约束:
$verticals = Vertical::all()->pluck('id', 'external_id'); DB::table('inclusions')->each(function ($row) use ($verticals) { $verticalIds = json_decode($row['verticals']); $matchingVerticalIds = $verticals->intersect($verticalIds)->values()->all(); foreach ($matchingVerticalIds as $verticalId) { DB::table('inclusion_vertical')->insert([ 'inclusion_id' => $row['id'], 'vertical_id' => $verticalId, ]); } });
方案2:在模型中动态关闭迁移场景下的软删除
在Inclusion模型中添加逻辑,检测到当前处于迁移执行状态时,自动禁用软删除作用域:
use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\SoftDeletes; use Illuminate\Database\Eloquent\Builder; use Illuminate\Database\Eloquent\Scope; class Inclusion extends Model { use SoftDeletes; protected static function booted() { // 判断是否在执行迁移命令 if (app()->runningInConsole() && in_array(request()->command(), ['migrate', 'migrate:fresh'])) { // 移除默认的软删除作用域 static::removeGlobalScope('Illuminate\Database\Eloquent\SoftDeletingScope'); // 添加空作用域替代,避免原有逻辑冲突 static::addGlobalScope(new class implements Scope { public function apply(Builder $builder, Model $model) { // 无额外查询条件 } }); } } }
注意事项
- 不要修改旧迁移的Schema定义(比如在初始迁移中添加
softDeletes()),生产环境已执行过该迁移,修改会导致迁移失败;但修改迁移中的数据操作部分是安全的,生产环境不会重复执行已完成的迁移。 - 所有方案均无需区分测试与生产环境,保持代码一致性。
内容的提问来源于stack exchange,提问作者dawgbone98
相关产品推荐
相关产品推荐

