ThinkPHP6软删除默认自动排除已删数据,需用withTrashed()查全部、onlyTrashed()查已删数据,且字段类型和名称配置必须正确匹配。

ThinkPHP6模型开启软删除后,条件过滤不是自动“按需生效”的,而是有固定规则:默认所有模型查询都会自动加上 delete_time IS NULL 条件,把已软删的数据彻底排除在外。想让条件过滤按你的意图工作,关键在于明确控制软删状态的开关位置和方式。
默认行为:所有查询自动排除软删数据
只要模型用了 use SoftDelete;,下面这些写法都等效于加了 AND delete_time IS NULL:
User::find(1)User::where('status', 1)->select()User::with('profile')->find(1)
哪怕数据库里那条记录的 delete_time 是一个有效时间戳,它也不会出现在结果中——这不是漏查,是框架主动拦截。
显式包含软删数据:withTrashed() 必须放链首
要查“全部数据(含已软删)”,必须用 withTrashed(),且它得是查询链的第一个方法:
立即学习“PHP免费学习笔记(深入)”;
- ✅ 正确:
User::withTrashed()->where('status', 1)->select() - ❌ 错误:
User::where('status', 1)->withTrashed()->select()(where已触发条件构建,withTrashed失效) - ❌ 错误:
User::withTrashed()->where('delete_time', '>', 0)->select()(逻辑矛盾,withTrashed已放开所有,再加该条件无意义)
注意:withTrashed() 只影响当前模型,关联模型(如 posts)仍按自身软删规则过滤,需单独调用 ->withTrashed()。
只查软删数据:onlyTrashed() 替换为 IS NOT NULL
要专门筛选已被软删的记录,用 onlyTrashed(),它会把默认的 IS NULL 条件替换成 IS NOT NULL:
-
User::onlyTrashed()->select()→ 查所有delete_time非空的用户 -
User::onlyTrashed()->where('name', 'like', '%admin%')->select()→ 在已软删数据中再筛名称
这个方法不能和 withTrashed() 连用;若连写,后者会覆盖前者,最终变成查全部。
字段与类型配置不当会导致条件失效
如果 delete_time 字段在数据库中是 VARCHAR 类型,或模型没配 $type(整型时间戳场景),会导致 IS NOT NULL 判断失准,看起来“条件没起作用”:
- 字段必须允许
NULL,类型推荐DATETIME或INT - 若用
INT存时间戳,模型中必须声明:protected $type = ['delete_time' => 'integer']; - 字段名不叫
delete_time?那就得在模型里明确写:protected $deleteTime = 'deleted_at';
否则,delete() 可能真删,查询也可能完全绕过软删逻辑——不是功能坏了,是配置没对齐。



















