软删除模型恢复需同时满足三条件:已软删(deleted_at非null)、未被逻辑锁死(如关联存在且作用域未过滤)、业务约束仍有效(如唯一性、关联完整性);须用withTrashed()查询并调canRestore()校验。

软删除模型怎么判断还能不能恢复
只有被软删除、且没被真正清除(即 deleted_at 不为 null),才可能恢复;但更重要的是:它得没被其他逻辑“锁死”——比如关联数据被硬删、外键约束失效,或自定义作用域屏蔽了该记录。
-
withTrashed()是必须的,否则find()或where()根本查不到软删记录 - 查到后别急着
restore(),先用$model->trashed()确认状态,避免对非软删模型误操作 - 如果模型启用了全局作用域(如多租户
tenant_id过滤),要确保作用域没把这条软删记录过滤掉——否则find()返回null,你以为“不存在”,其实是“查不到”
Laravel 恢复前怎么验证业务规则是否还成立
软删只是把 deleted_at 设为时间戳,不保证关联、唯一性、状态流转等业务约束依然有效。恢复失败往往不是框架报错,而是你自己的逻辑崩了。
- 检查关键字段是否仍合法:比如
email是否被新用户占用了?用User::where('email', $user->email)->where('id', '!=', $user->id)->exists()验证 - 确认关联是否存在:比如订单恢复前,得确保所属用户还在(且没被硬删),否则
$order->user->name会抛Trying to get property 'name' of non-object - 避免事务中恢复后立刻触发监听器/观察者——某些监听器可能依赖
created_at或其它软删期间变更的字段,结果读到过期值
restore() 失败却没报错?常见静默陷阱
restore() 默认返回布尔值,但 Laravel 不抛异常,很多开发者以为“没报错=成功”,其实可能因数据库约束、事件中断或自定义 restoring 事件返回 false 而终止。
- 在
restoring事件里return false会阻止恢复,但不会提示原因——加日志:Log::info('Restoring user', ['id' => $user->id]) - MySQL 的
ON DELETE CASCADE对软删无效,但如果关联表用了硬删策略,恢复主模型时,子记录已不存在,业务逻辑可能出错 - 使用
forceDelete()后再调restore()?不行。forceDelete()真删了行,restore()找不到记录,静默失败
怎么写一个可靠的「可恢复」检查方法
别依赖单一状态判断,要把软删状态、业务约束、关联完整性打包成可复用的校验逻辑。
public function canRestore(): bool
{
if (! $this->trashed()) {
return false;
}
// 检查邮箱是否冲突
if (User::where('email', $this->email)
->where('id', '!=', $this->id)
->exists()) {
return false;
}
// 检查关联用户是否还存在且未软删(根据业务需要决定是否允许关联也软删)
if (! $this->user || $this->user->trashed()) {
return false;
}
return true;
}
这个方法不自动修复问题,只回答“现在能不能安全恢复”。真实场景里,它该是恢复操作前的守门员,而不是补救工具。
软删除恢复最麻烦的从来不是代码怎么写,而是“删的时候没想好谁依赖它,恢复时才发现一堆隐性前提已经塌了”。每次 restore() 前,都得当它是第一次创建一样重新过一遍业务上下文。


















