ThinkPHP软删除恢复失败的主因是未配置deleted_at时间字段及未用withTrashed()查询已删数据;须同时满足模型启用SoftDelete、数据库字段为DATETIME/TIMESTAMP且允许NULL、操作前调用withTrashed()。

软删除字段没加 deleted_at 会导致恢复失败
ThinkPHP 的软删除依赖模型中定义的 delete_time 字段(默认名,也可自定义),但很多开发者直接在数据库加了 is_deleted 这类布尔字段,结果调用 restore() 时毫无反应——因为框架根本没识别到这是软删除模型。
必须同时满足两个条件:模型开启软删除 + 数据表含时间类型字段:
- 模型类里要声明
use SoftDelete;并配置protected $deleteTime = 'deleted_at'; - 数据库字段
deleted_at类型必须是DATETIME或TIMESTAMP,不能是TINYINT或CHAR - 如果用的是 ThinkPHP 6.0+,还要确认
protected $autoWriteTimestamp = true;已启用,否则restore()可能清不掉deleted_at值
restore() 不生效?检查查询是否带了 withTrashed()
软删除数据默认被全局作用域过滤掉,restore() 内部其实只是把 deleted_at 设为 NULL。但如果当前查询没提前拉出已删除记录,就等于“对不存在的数据执行恢复”。
正确姿势是先查出软删除状态的记录,再调用 restore():
// 错误:直接查,查不到已删数据
User::where('id', 123)->restore(); // 无效果
// 正确:先 withTrashed() 拉出,再恢复
User::withTrashed()->where('id', 123)->restore();
// 或者更安全地链式操作
User::withTrashed()->find(123)?->restore();
注意:ThinkPHP 6.1+ 支持 findOrEmpty(),推荐用它替代 find() 防止空对象调用异常。
批量恢复要小心事务和主键类型
用 restore() 批量恢复(如 where(...)->restore())时,ThinkPHP 默认走 UPDATE 语句,但有两处容易翻车:
- 如果主键不是
id(比如叫user_id),且没在模型里显式声明protected $pk = 'user_id';,批量恢复可能更新错行 - 批量操作不在事务内,万一中间出错,部分数据恢复、部分仍软删除,状态不一致
- MySQL 严格模式下,若
deleted_at字段设了NOT NULL DEFAULT '0000-00-00 00:00:00',restore()会因无法写入NULL而报错Invalid datetime format
建议做法:显式开启事务 + 确认字段允许 NULL:
Db::transaction(function () {
User::withTrashed()
->where('status', 0)
->restore();
});
硬删除后无法恢复,别把软删除当备份
很多人误以为开了软删除就等于数据永不丢失,其实只要执行过 forceDelete() 或原生 SQL 的 DELETE FROM,记录就从磁盘消失了。restore() 只对 delete() 触发的软删有效。
真正需要可逆操作的场景,得配合额外机制:
- 定期导出软删除数据到归档表(用
select()->insertAll()) - 关键业务加操作日志,记录谁、何时、删了哪条
- 管理后台恢复入口务必二次确认,并限制 IP / 权限,避免误点
软删除不是保险柜,它只解决“手滑删错”的分钟级回退,撑不过一次部署或一次误执行 forceDelete()。
php免费学习视频:立即使用
踏上前端学习之旅,开启通往精通之路!从前端基础到项目实战,循序渐进,一步一个脚印,迈向巅峰!











