restore()只能在已软删除的模型实例上调用,需先用onlytrashed()查出记录再调用实例方法;不支持查询构造器链式调用,批量恢复须遍历或用db直接更新。

restore() 只能作用于已软删除的模型实例
直接 Staff::where('status', 1)->restore() 会报错 —— restore() 不是查询构造器方法,它必须在「已加载且 delete_time 非 null」的模型实例上调用。ThinkPHP 的 restore() 内部只清空 delete_time 字段,不校验其他条件,也不支持链式 where。
常见错误现象:Call to undefined method think\db\Query::restore() 或静默失败(数据没恢复)。
- 正确做法:先用
onlyTrashed()查出目标记录,再调用实例方法restore() - 如果要按业务字段筛选(比如只恢复
is_verified = 1的软删用户),必须把条件写进onlyTrashed()的链式查询里 - 注意:
onlyTrashed()本身已强制加了delete_time IS NOT NULL,不能再重复加该条件
$user = Staff::onlyTrashed()
->where('is_verified', 1)
->where('age', '>', 18)
->find();
if ($user) {
$user->restore(); // ✅ 成功恢复
}
批量恢复必须手动遍历或改用 update
ThinkPHP 没有内置的 restoreAll() 或 onlyTrashed()->update() 批量恢复方法。试图用 Staff::onlyTrashed()->where(...)->restore() 依然无效。
性能影响明显:用 select()->each() 遍历恢复,每条记录触发一次 UPDATE 查询;大数据量时容易超时或锁表。
- 推荐方案:绕过模型,用 Db 直接更新字段(前提是确认字段类型和值安全)
- 若必须走模型逻辑(比如要触发
after_restore事件),只能用集合遍历,但需加 try/catch 和分页控制 - 千万别用
destroy()或delete(true)去“反向硬删”来模拟恢复——这会彻底丢数据
// 安全批量恢复(Db 方式,跳过模型事件)
\think\Db::name('staff')
->where('delete_time', '', null)
->where('is_verified', 1)
->update(['delete_time' => null]);
// 或走模型(小批量可用)
Staff::onlyTrashed()
->where('is_verified', 1)
->limit(100)
->select()
->each(function ($item) {
$item->restore();
});
restore() 失败的三个典型原因
恢复操作看似简单,但实际中常因配置或字段细节失败,且无明确报错。
-
delete_time字段类型不匹配:比如设为int类型但没配$type,写入时存成字符串"0",查出来永远不为null,onlyTrashed()就找不到记录 - 模型没正确 use
SoftDeletetrait,或$deleteTime拼错(如写成$deleTime),导致软删标记根本没生效,自然也谈不上恢复 - 事务中恢复后未 commit,或恢复前该记录已被其他进程物理删除(
force()->delete()),find()返回 null,restore()调用在 null 上直接报错
检查顺序建议:先确认数据库里 delete_time 确实是非 null 值 → 再查模型是否启用 softdelete → 最后看 onlyTrashed()->find() 能否取到实例。
恢复后数据仍查不到?默认查询仍过滤
调用 $user->restore() 成功后,Staff::get(123) 还是返回 null?不是恢复失败,是 ThinkPHP 默认所有查询(get、all、find)仍自动加 AND delete_time IS NULL —— 它只认字段值,不感知你刚执行过 restore()。
这容易让人误以为“恢复没生效”,其实只是缓存或查询逻辑没刷新。真实数据已写回 delete_time = null,但下次查还得走正常流程。
- 验证方式:直接查数据库,看
delete_time是否已变回NULL - 开发期调试:临时加
withTrashed()查全量,确认恢复前后字段变化 - 注意:
restore()不会自动 reload 实例属性,恢复后想读新状态得重新get()一次
真正容易被忽略的是:软删除不是开关,而是一套字段 + 查询拦截 + 方法约束的组合。任何一个环节断开,restore() 就成了无源之水。
php免费学习视频:立即使用
踏上前端学习之旅,开启通往精通之路!从前端基础到项目实战,循序渐进,一步一个脚印,迈向巅峰!











