Laravel 模型查询与原生 DB 查询结果不一致的根源:软删除机制解析

2次阅读

Laravel 模型查询与原生 DB 查询结果不一致的根源:软删除机制解析

laravel 中使用 eloquent 模型查询返回空结果,而 db::table() 却能正常获取数据,通常是因为模型启用了软删除(softdeletes),导致已“删除”的记录被自动排除,而原生查询不受影响。

laravel 中使用 eloquent 模型查询返回空结果,而 db::table() 却能正常获取数据,通常是因为模型启用了软删除(softdeletes),导致已“删除”的记录被自动排除,而原生查询不受影响。

在 Laravel 开发中,你可能会遇到这样一种看似“诡异”的现象:对同一张表执行完全相同的条件查询(如 whereIn(‘attendance_date_id’, [925, 926])),使用 DB::table(‘attendances’) 能查出多条记录,而使用 Attendance::class 模型却始终返回空集合:

Route::get('v1/testapi', function (Request $request) {     return [         'withDB' => DB::table('attendances')             ->select("id", "attendance_date_id", "remarks")             ->whereIn('attendance_date_id', [925, 926])             ->get(),         'withModel' => Attendance::select("id", "attendance_date_id", "remarks")             ->whereIn('attendance_date_id', [925, 926])             ->get()     ]; });

输出显示 withDB 包含 5 条有效数据,而 withModel 为空数组 —— 这并非 bug,而是 Laravel Eloquent 的默认行为设计

根本原因:SoftDeletes 特性自动过滤

当你在 Attendance 模型中引入了 use SoftDeletes; 并定义了 $dates = [‘deleted_at’];(或 Laravel 9+ 中的 $casts = [‘deleted_at’ => ‘datetime’];),Eloquent 会自动为所有常规查询添加隐式约束:

WHERE deleted_at IS NULL

这意味着:即使数据库中存在 attendance_date_id IN (925, 926) 的记录,只要其 deleted_at 字段非 NULL(即已被“软删除”),Eloquent 就会将其静默排除,不返回给调用方。

而 DB::table() 是底层查询构建器,不感知模型逻辑,也不应用任何全局作用域(Global Scopes),因此它忠实地返回所有匹配的物理行——包括软删除的数据。

验证与调试方法

你可以快速验证是否存在软删除记录:

// 查看是否启用了 SoftDeletes dd(class_uses(Attendance::class)); // 应包含 IlluminateDatabaseEloquentSoftDeletes  // 手动查询软删除数据(绕过默认过滤) $softDeleted = Attendance::withTrashed()     ->select("id", "attendance_date_id", "remarks", "deleted_at")     ->whereIn('attendance_date_id', [925, 926])     ->get();  // 仅查已被软删除的记录 $onlyTrashed = Attendance::onlyTrashed()     ->whereIn('attendance_date_id', [925, 926])     ->get();

若 withTrashed() 返回了预期数据,即可确认问题根源。

正确处理建议

  • 日常查询:保持默认行为(安全、符合业务逻辑),避免误展示已归档/删除的数据;
  • 需要恢复或管理时:显式调用 withTrashed() 或 onlyTrashed();
  • ⚠️ 慎用 withoutGlobalScopes():它会禁用所有全局作用域(包括软删除、租户隔离等),可能引发权限或数据一致性风险;
  • ? 不要移除 SoftDeletes 仅为了“让查询有结果”——这违背软删除的设计初衷,应通过业务逻辑而非绕过机制来解决。

小结

对比维度 DB::table() Eloquent Model
是否感知软删除 是(自动添加 WHERE deleted_at IS NULL
是否受全局作用域影响
适用场景 快速脚本、报表、迁移、调试 主业务逻辑、CRUD、需生命周期管理的场景

理解这一机制,不仅能快速定位类似“查询失灵”问题,更是掌握 Laravel 数据层抽象能力的关键一步。

text=ZqhQzanResources