误删数据后restore()无效,需满足三个前提:①模型启用SoftDelete并定义$deleteTime;②低版本需设$useSoftDelete=true;③刷新schema缓存;同时delete_time字段必须为NULLABLE的DATETIME/TIMESTAMP类型。

误删线上用户数据后,直接调用restore()却没反应,数据库里delete_time字段依然非空,业务订单状态错乱,客户投诉电话已经打爆——这不是bug,是你没踩对TP6.0软删除恢复的三个致命前提。
确认模型真正启用了SoftDelete
第一步:打开你的模型文件,检查是否同时满足以下三项,缺一不可:
① 引入了use think\model\concern\SoftDelete;
② 类内声明了use SoftDelete;
③ 设置了protected $deleteTime = 'delete_time';
第二步:如果是ThinkPHP 6.0之前的低版本(如6.0.0~6.0.7),必须显式开启开关:protected $useSoftDelete = true;。6.0.8+可省略,但写上更保险。
第三步:运行php think optimize:schema刷新模型缓存,避免旧缓存导致配置未生效。
注意:只写了use SoftDelete;但漏掉$useSoftDelete = true;(低版本)或$deleteTime字段定义,【restore()会静默返回false,不报错也不写库】。
检查delete_time字段的数据库定义
执行SQL查字段结构:DESCRIBE user;(把user换成你的真实表名)
确认delete_time字段类型为DATETIME或TIMESTAMP,且Null列值为YES,Default为NULL。
如果显示NO或默认值是'1970-01-01'这类固定时间,框架尝试写入NULL时会被MySQL直接拒绝,而PHP层捕获不到这个错误,【restore()看似执行成功,实则数据库毫无变化】。
修复方法:执行ALTER语句重置字段允许为空:ALTER TABLE `user` MODIFY COLUMN `delete_time` DATETIME NULL DEFAULT NULL;
单条数据恢复:先查再restore
方法一(推荐):
① 从软删池中精确取出目标记录:$user = UserModel::onlyTrashed()->find(123);
② 调用实例方法恢复:$user->restore();
③ 检查返回值:if (!$user->restore()) { throw new Exception('恢复失败,请检查模型配置与字段定义'); }
方法二(绕过模型事件,直写数据库):UserModel::where('id', 123)->update(['delete_time' => null]);
这一步跳过所有验证和restored事件,仅适用于紧急救火且无需触发回调的场景。
批量恢复:绝不能链式调用restore()
错误写法:UserModel::onlyTrashed()->where('status', 1)->restore();
这是无效操作——restore()只作用于单个模型实例,而onlyTrashed()->where()返回的是查询构造器,不是模型集合。
正确做法分两步:
第一步:提取ID列表:$ids = UserModel::onlyTrashed()->where('status', 1)->column('id');
第二步:批量更新字段:UserModel::whereIn('id', $ids)->update(['delete_time' => null]);
若需触发restored事件,改用分块处理:UserModel::onlyTrashed()->where('status', 1)->chunk(500, function ($users) { $users->each->restore(); });,注意闭包内必须判断每条$user->restore()的返回值。
关键提醒:【批量恢复必须包裹在事务中】,否则某条失败会导致部分数据已恢复、部分仍软删,状态撕裂无法回滚。
关联数据恢复:不会自动连带,必须手动逐层处理
$user->restore()只清空user表的delete_time字段,订单、地址、日志等关联表哪怕也启用了SoftDelete,也不会被连带恢复。
不能写:$user->with(['orders'])->restore();——with()默认过滤软删记录,根本查不到任何订单。
正确路径:
① 查出该用户的软删订单:$orders = $user->orders()->onlyTrashed()->select();
② 遍历恢复:$orders->each->restore();
量大时建议用Db直写:Db::name('order')->where('user_id', $user->id)->whereNotNull('delete_time')->update(['delete_time' => null]);,但会跳过事件和验证逻辑。



















