ThinkPHP软删除失效主因是delete_time字段类型错误、未启用SoftDelete trait或配置不当;查已删数据用withTrashed(),仅查已删用onlyTrashed();关联查询失效需关联模型也启用软删除或加withoutGlobalScopes();restore()失败多因字段约束或修改器误处理。

软删除字段没生效,查出来还是被删的数据
ThinkPHP 的软删除默认靠 delete_time 字段判断,但这个字段必须是数据库真实存在的字段,且模型里得显式启用。如果查出来的数据包含已“删除”的记录,大概率是以下情况之一:
-
delete_time字段在数据库中类型不是datetime或int(时间戳),比如误设为varchar,会导致自动过滤失效 - 模型类没继承
SoftDeletetrait,或没在$autoWriteTimestamp和$softDelete上做正确配置 - 手动写了
where('delete_time', 'null')这类硬条件,反而绕过了框架的软删除逻辑
正确做法是在模型顶部加上:
use think\model\concern\SoftDelete;并在类定义中声明:
use SoftDelete;同时确保
$deleteTime 与数据库字段名一致(默认就是 delete_time)。
想临时查出已软删除的数据,该怎么写
默认所有查询都会自动加 delete_time IS NULL 条件,要临时绕过,不能用 where('delete_time', null) —— 这会生成 IS NULL,但可能和软删除机制冲突。最稳妥的是用 withTrashed() 方法:
$users = User::withTrashed()->select();
它会移除软删除自动附加的 WHERE 条件,查出全部记录(含 delete_time != null 的)。如果只想查已被软删除的,用 onlyTrashed():
立即学习“PHP免费学习笔记(深入)”;
$deletedUsers = User::onlyTrashed()->select();
注意:这两个方法只对当前链式调用生效,不影响其他查询。
软删除后关联查询失效或数据不全
当主模型启用了软删除,而关联模型(比如 hasMany)没启用时,关联查询可能返回空,因为 ThinkPHP 默认会对所有参与查询的模型应用软删除规则。常见表现是:User::with('posts')->find(1) 返回的 posts 是空数组,即使数据库里有未删除的帖子。
原因通常是关联模型(如 Post)没开启软删除,但主模型的查询上下文把软删除条件“传染”过去了。解决办法有两个:
- 在关联定义里显式关闭软删除过滤:
public function posts() { return $this->hasMany(Post::class)->withoutGlobalScopes(); } - 更推荐的方式:让关联模型也启用
SoftDeletetrait,并确认其$deleteTime字段存在且类型正确
别依赖“主模型删了,关联就自动不可见”——ThinkPHP 不做级联软过滤,得每个模型自己管好自己的 delete_time。
restore() 失败但没报错,数据还是查不到
restore() 方法本身不会抛异常,但它只更新 delete_time 字段为 null(或默认值),不触发事件、不校验字段权限、也不刷新模型状态。如果执行后依然查不到,先检查:
- 是否用了
save()或update()之后才调restore()?错误顺序会导致覆盖操作,比如先save(['delete_time' => 0])再restore(),结果仍是 0 - 数据库字段是否设置了
NOT NULL约束?如果是,restore()会因 SQL 报错失败,但 ThinkPHP 可能静默吞掉错误(需开debug模式看日志) - 有没有在模型里重写了
deleteTime的访问器或修改器?比如加了setDeleteTimeAttr却没处理null场景,导致restore()实际写入了字符串'null'而非真正的NULL
最保险的验证方式是直接查数据库:SELECT delete_time FROM user WHERE id = 123;,确认值确实是 NULL 而不是 0、空字符串或 '0000-00-00 00:00:00'。



















