ThinkPHP软删除需显式启用SoftDelete trait并配置deleteTime字段,字段须为可空类型;withTrashed()和onlyTrashed()仅影响当次查询且须在select前调用;restore()是实例方法,须先查出软删除记录再调用,并检查返回值。

软删除字段没生效,delete() 直接删库了
ThinkPHP 的软删除不是默认开启的,哪怕你加了 deleted_at 字段,不显式声明,delete() 就是物理删除。核心在模型里要启用 SoftDelete trait,并配置好字段名。
- 模型类必须 use
think\model\concern\SoftDelete - 加上
use SoftDelete;声明(注意不是use \think\model\concern\SoftDelete;,命名空间已导入) - 如果字段不是默认的
deleted_at,得在模型里加protected $deleteTime = 'is_deleted';(值为字段名,类型建议 int 或 datetime) - 字段类型要是可为空的,比如 MySQL 里
deleted_at DATETIME NULL,否则软删除会因 NOT NULL 约束报错
withTrashed() 和 onlyTrashed() 怎么选
这两个方法控制查询范围,但容易混淆:它们只影响当前这次查询,不会改变模型默认行为;而且调用时机很关键——必须在 select() 或 find() 之前链式调用,否则无效。
-
withTrashed():查所有数据,包括已软删除的(相当于去掉WHERE deleted_at IS NULL) -
onlyTrashed():只查已软删除的(相当于WHERE deleted_at IS NOT NULL) - 恢复数据前务必先用
onlyTrashed()确认目标存在,比如:User::onlyTrashed()->where('id', 123)->find() - 别在分页或关联查询里漏掉它——
withTrashed()不会自动透传到关联模型,每个关联都得单独加
恢复数据用 restore(),但失败不报错
restore() 是模型实例方法,不是静态方法,且它默认静默失败:如果记录不存在、或根本没软删除过,它不会抛异常,只是返回 false 或空结果。
- 必须先查出软删除状态的记录,再调用实例的
restore(),不能直接User::where('id', 123)->restore()(这会报错:Call to undefined method) - 正确写法:
$user = User::onlyTrashed()->find(123); if ($user) $user->restore(); - 检查返回值:
if (false === $user->restore()) { /* 失败,可能是 deleted_at 字段被改过或权限问题 */ } - 注意事务:如果恢复后还要做其他操作,记得手动开启事务,避免部分成功
软删除后关联数据没动,怎么办
ThinkPHP 默认不级联软删除或恢复。比如订单软删除了,它的订单项(order_items)还是正常状态,不会自动标记,也不会自动恢复。
立即学习“PHP免费学习笔记(深入)”;
- 没有内置级联选项,得手动处理:恢复主记录后,自己查出关联数据并逐个
restore() - 如果依赖外键或业务强一致性,建议在删除/恢复逻辑里显式控制,比如重写模型的
delete()和restore()方法 - 慎用触发器或数据库级级联——TP 的软删除靠 PHP 层判断字段,数据库触发器无法感知
deleted_at赋值逻辑,反而容易冲突 - 索引要考虑
deleted_at字段:高频查询未删除数据时,加联合索引如(status, deleted_at)能明显提速
软删除看着简单,真正落地时字段定义、查询链路、关联一致性、恢复校验这四块最容易出问题。尤其是 restore() 必须基于已查出的软删除实例,这点很多人第一次就踩空。



















