withtrashed只是查询作用域,不修改数据;恢复必须调用restore()方法清空deleted_at并触发事件,且需refresh()刷新模型、显式配置关联withtrashed()、批量操作须加事务。

Hyperf 的软删除恢复不能靠 withTrashed 实现——它只是“查得到”,不是“恢复操作”。真要恢复记录,得调用模型的 restore() 方法。
为什么 withTrashed 不能恢复数据
withTrashed 是查询作用域,只影响 SQL 的 WHERE 条件(比如去掉 deleted_at IS NULL),不会修改任何字段。它返回的是已软删除的模型实例,但这些实例的 deleted_at 仍非空,状态仍是“已删除”。
- 常见错误:查出记录后直接
$model->save(),结果deleted_at还在,下次find还是查不到 - 正确做法:必须显式调用
restore(),它会把deleted_at设为NULL并触发事件 - 注意:Hyperf 的
restore()不会自动刷新模型属性,调用后建议重新refresh()或重新查询
恢复单条记录的实操步骤
先查出软删除记录,再调用 restore(),最后验证 deleted_at 是否清空:
// ✅ 正确写法
$user = User::withTrashed()->find(123);
if ($user && $user->trashed()) {
$user->restore();
$user->refresh(); // 确保 deleted_at 变成 null
}
// ❌ 错误写法(不生效)
$user = User::withTrashed()->find(123);
$user->deleted_at = null;
$user->save(); // 没触发 restore 逻辑,事件不触发,且可能被中间件/监听器拦截
批量恢复要注意事务和主键类型
Hyperf 默认不支持 restore() 批量调用(不像 Laravel 有 restoreByIds),必须手动遍历或写原生 SQL。用循环时务必加事务,尤其涉及关联数据一致性场景:
- 主键是字符串(如 UUID)时,
whereIn查询需确认数据库驱动是否支持字符串数组绑定 - 大量记录恢复建议分批(比如每 100 条一个事务),避免超时或内存溢出
- 如果模型启用了
SoftDeletes但没定义$dates = ['deleted_at'],restore()可能失败(Hyperf 2.2+ 对日期字段校验更严格)
恢复后关联查询仍失效?检查全局作用域
即使调用 restore() 成功,后续用 User::with('posts') 查关联,posts 表若也软删除,默认仍过滤掉已删记录。这不是 bug,是设计行为:
- 关联关系默认不继承父模型的软删除状态,需显式加
withTrashed()到关系定义里 - 例如在
User模型中:public function posts() { return $this->hasMany(Post::class)->withTrashed(); } - 或者临时链式调用:
$user->posts()->withTrashed()->get()
软删除恢复本质是字段重置 + 事件触发,Hyperf 里没有魔法开关。最常被忽略的是:恢复后没刷新模型、关联没显式启用软删除查询、事务没包裹批量操作——这三处出错,看起来像“恢复失败”。











