ThinkPHP软删除由SoftDelete trait自动注入全局作用域实现,baseScope不参与也不应手动添加delete_time过滤;否则会导致冲突或覆盖withTrashed()效果。

ThinkPHP 模型作用域(baseScope)本身不参与软删除过滤,真正起作用的是 SoftDelete trait 注入的全局作用域;如果你在 baseScope 里手动加了 where('delete_time', null),反而会导致重复过滤或冲突。
软删除过滤不是靠 baseScope 实现的
很多人误以为要重写 baseScope() 才能实现软删除过滤,其实完全不需要。ThinkPHP 6 的 SoftDelete 是通过 trait 自动注册一个隐藏的全局作用域(类似 Laravel 的 global scope),在所有查询生成 SQL 前自动注入 WHERE delete_time IS NULL 条件。
-
baseScope()是你自定义的通用查询约束,比如默认查 status=1 的用户,它和软删除逻辑无关 - 如果你在
baseScope()里又写了$query->where('delete_time', null),会导致条件叠加,可能查不到数据(尤其当字段类型不匹配时) - 真正影响软删行为的是模型是否启用
use SoftDelete;+$useSoftDelete = true;+ 字段存在且类型正确
baseScope 与 withTrashed() 冲突怎么办
当你调用 User::withTrashed()->select() 却仍然查不到已软删记录,大概率是 baseScope() 里硬编码了删除状态判断,把 withTrashed() 的“移除过滤”给覆盖了。
- 检查模型中是否重写了
baseScope(),删掉其中对delete_time或类似字段的手动where -
withTrashed()必须放在链式调用最前面,例如User::withTrashed()->where('status', 1)->select()才有效;User::where('status', 1)->withTrashed()无效 - 如果必须保留
baseScope()中的状态过滤,建议改用闭包方式动态控制:->where(function ($q) { $q->where('status', 1)->whereNull('delete_time'); }),避免和软删机制耦合
关联模型的作用域不会继承主模型的软删状态
主模型调用 withTrashed() 不会影响关联模型的查询逻辑——哪怕你在主模型里写了 baseScope(),子模型也只认自己的 SoftDelete 配置和它自己的 baseScope()。
立即学习“PHP免费学习笔记(深入)”;
- 例如
User::withTrashed()->with('orders')中,orders仍按自身模型规则过滤(即只查delete_time IS NULL的订单) - 要让关联也包含软删数据,必须单独干预:要么在关联定义里加
->withTrashed(),要么在with()闭包里写function ($q) { $q->withTrashed(); } - 如果关联模型没启用
SoftDelete,withTrashed()调用不会报错,但也不起作用——这点容易被忽略
真正决定软删行为的是字段是否存在、类型是否匹配、trait 是否启用这三者对齐;baseScope 是个干扰项,不是实现软删的必要环节,强行掺和反而容易踩坑。



















