跳转到主内容
极星编程网:以代码为星,赴技术山海!

Laravel如何做模型强制删除_Laravel绕过软删除彻底清除【方法】

forceDelete() 是模型实例方法,用于物理删除软删除记录并触发 deleting/deleted 事件;不支持批量调用,批量删除需用 DB::table() 或原生 SQL;并发安全删除应加 whereNotNull('deleted_at') 条件或使用 lockForUpdate()。 用
forceDelete()
彻底删掉软删除模型 软删除模型默认只是打个标记(
deleted_at
非空),不走真实 DELETE SQL。要真正从数据库删掉,必须显式调用
forceDelete()
—— 它会跳过软删除逻辑,执行物理删除。 常见错误是直接写
$model->delete()
,结果发现记录还在表里,只是
deleted_at
被设了时间;或者误以为
DB::table()->where()->delete()
能绕过,但其实它根本不会触发模型事件、也不会调用
bootSoftDeletes()
相关逻辑,连关联清理都得自己手写。
forceDelete()
是模型实例方法,必须先查出模型(如
User::withTrashed()->find(123)
),再调用 它会触发
deleting
和
deleted
事件,适合需要清理附件、缓存等场景 不支持批量强制删除:没有
where(...)->forceDelete()
这种写法,批量只能用原生查询或循环调用 批量物理删除只能靠
DB::table()
或原生 SQL 想一次性删掉几百条软删除记录?别指望
User::onlyTrashed()->forceDelete()
—— 这行不通,Laravel 不提供模型作用域的强制删除链式方法。 正确做法是绕过模型,直连数据表。但要注意:这会跳过所有模型生命周期钩子(
deleting
、
deleted
)、Eloquent 访问器、以及
SoftDeletes
trait 的任何干预。 用
DB::table('users')->whereNotNull('deleted_at')->delete()
最简洁,也最快 如果要保留日志或触发清理逻辑,得先
select id
拿到主键列表,再用集合分块调用
forceDelete()
MySQL 中
DELETE
不释放空间,大量删除后建议后续执行
OPTIMIZE TABLE users
恢复已软删除模型前,别漏掉
restore()
的条件检查 很多人在做“强制删之前先确认是否已被软删除”时,直接
if ($user->trashed()) { $user->forceDelete(); }
,看似合理,但容易忽略并发风险:两次请求之间,另一进程可能已调用
restore()
,导致误删活跃数据。 更稳妥的做法是在数据库层面加条件判断,用原子操作确保安全。 推荐用
User::where('id', 123)->whereNotNull('deleted_at')->delete()
,一行完成「只删已软删除的」 如果必须用模型操作,先
lockForUpdate()
再查再删:
User::withTrashed()->where('id', 123)->lockForUpdate()->first()
restore()
本身不会报错,哪怕模型没软删除过;但它只会清空
deleted_at
,不会回滚其他字段变更 硬删后关联数据不会自动清理,得自己兜底 用
forceDelete()
或
DB::table()
删主模型时,Laravel 的
onDelete('cascade')
外键约束只对数据库生效,而 Eloquent 的
deleting
事件监听、
HasOne/HasMany
的
->onDelete()
方法(如
cascade
)全都不触发。 也就是说:用户被物理删了,它的头像文件、订单记录、日志条目,一个都不会少 —— 除非你提前写了对应逻辑。 在模型的
deleting
事件里手动删关联(适用于
forceDelete()
场景) 数据库建外键时明确加
ON DELETE CASCADE
,这样
DB::table()
删除才真正级联 用迁移命令
php artisan schema:dump
检查当前外键定义,别假设它存在 最麻烦的不是删不掉,而是删得“太干净”又没留痕:物理删除后,
deleted_at
字段消失,连“这条数据曾经存在过”的证据都没了。真要审计或回滚,得靠备份或 binlog。

相关文章