必须用 force() 配合 delete() 或 destroy() 物理删除软删数据,直接 sql 或 db::delete() 会绕过软删逻辑且丢失事件钩子,force() 仅为链式标记而非独立操作。

想物理删除软删数据,必须用 force() 配合 delete() 或 destroy(),不能直接写 SQL 或用 Db::delete()。
force() 是唯一合法的“真删”入口
force() 不是独立操作,它只是个链式标记,告诉后续的 delete() 或 destroy() “这次别软删,给我硬删”。不加它,哪怕你查的是软删记录,调用 delete() 仍会再次更新 delete_time(变成新时间戳),而不是删行。
-
User::onlyTrashed()->where('id', 123)->force()->delete()✅ 正确:先定位软删记录,再强制物理删除 -
User::destroy(123, true)✅ TP5/6 都支持,true参数等价于force() -
User::where('id', 123)->force()->delete()⚠️ 危险:若该记录未软删过,force()仍生效,直接物理删——但你本意可能是恢复或忽略 -
User::where('id', 123)->delete()❌ 默认软删;加了force()才真删,漏写就白忙
Db 类操作一律绕过软删逻辑
所有 Db::name('user')->where(...)->delete()、Db::table('user')->delete() 都跳过模型层,不触发 SoftDelete trait,也不管 delete_time 字段是否存在——它们就是原生 DELETE 语句,执行即物理删除,且无法被 withTrashed() 影响。
- 误用
Db::name('user')->where('delete_time', 'not null')->delete()会直接清空回收站,但没走模型事件(deleting/deleted),日志、关联清理全丢失 - 想保留钩子又批量真删?只能先
onlyTrashed()->chunk()查出 ID 列表,再用destroy($ids, true) - TP6 中
User::onlyTrashed()->force()->delete()支持批量,TP5 需显式传数组给destroy()
restore() 后立刻 force() 删除?别省这一步
有人想“先恢复再真删”,比如 $user->restore()->force()->delete() —— 这不行。restore() 返回的是模型实例,但 force() 是查询构造器方法,不能链在实例后面。而且恢复再删毫无意义,纯属多此一举。
- 正确做法:确认是软删状态后,直接
onlyTrashed()->force()->delete() - 如果业务要求“恢复后审核再删”,那必须分两步:先
restore(),等人工确认后再走force()->delete() - 注意事务:
force()操作不可逆,建议包裹在Db::transaction()中,尤其涉及关联表清理时
真正难的不是命令怎么写,而是得时刻记住:force() 必须和模型方法配对使用,且只在明确需要物理删除时才启用——多数场景下,软删数据留着比删掉更安全。
php免费学习视频:立即使用
踏上前端学习之旅,开启通往精通之路!从前端基础到项目实战,循序渐进,一步一个脚印,迈向巅峰!











