Model::destroy()只接受主键值或闭包,不支持链式调用或非主键条件;正确做法是先查ID再销毁,或直接用Db::delete()绕过模型层执行原生删除。

Model::destroy() 只认主键,非主键字段删不了
很多人写 UserModel::where('status', 0)->destroy(),直接报错:Call to undefined method destroy()。因为 destroy() 是静态方法,不接受查询构造器链式调用,只接收主键值(单个 ID、ID 数组、或闭包条件)。它内部会先查 ID,再删——但这个“查 ID”只支持按主键查,不支持任意字段。
常见错误现象:UserModel::destroy(['status' => 0]) 看似合理,实际会被当成主键值去查,导致 SQL 错误或删错数据。
- 正确做法是显式提取 ID:
$ids = UserModel::where('status', 0)->column('id');,再UserModel::destroy($ids) - 如果模型没定义主键(比如视图表),
destroy()会失效,必须改用Db::name('user')->where('status', 0)->delete() -
destroy()会触发模型的deleting/deleted事件,适合需要业务钩子的场景
Db::delete() 不走模型逻辑,但最快最可控
当你要删大量数据、且不需要模型事件、验证、软删除逻辑时,Db::name('user')->where('status', 0)->delete() 是最直接的选择。它生成原生 DELETE 语句,绕过模型层,性能高,也不受软删除配置影响。
注意:它不调用任何模型方法,所以 soft_delete 字段不会被自动写入,也不会触发事件。如果你启用了软删除但又想真删,这是唯一可靠方式。
立即学习“PHP免费学习笔记(深入)”;
- 支持
in条件:Db::name('user')->where('id', 'in', [1,2,3])->delete() - 支持复合条件:
Db::name('user')->where('status', 0)->where('created_time', 'delete() - 返回值是影响行数(int),可用于判断是否真有数据被删
软删除批量操作必须用 destroy(true),不能靠 where()->delete()
启用 SoftDelete trait 后,UserModel::where('status', 0)->delete() 依然会走软删逻辑——但这是假的:它只对模型实例有效,对查询构造器无效,实际执行的是硬删(除非你重写了 delete 方法)。
真正安全的软删批量操作只有一种:UserModel::destroy($ids, true)。第二个参数 true 表示「强制软删」,即使传的是 ID 列表也会统一打上 delete_time 标记。
- 不加
true:默认行为是硬删(如果模型没启用软删除)或按主键软删(如果启用) - 要恢复数据,得先
UserModel::withTrashed()->where('status', 0)->select()查出,再逐条调用$model->restore() -
withTrashed()是查询时用的,不影响删除动作本身
别信 batchDelete(),TP6 已移除,TP5 也极少用
网上有些教程提到 Db::table('user')->batchDelete(),这在 TP6 中根本不存在;TP5 虽有,但仅限于极老版本(
它的底层就是封装了一次 DELETE ... IN (...),和手写 Db::delete() 没本质区别,反而多一层不可控封装。
- TP5 中若存在,需确认框架版本 ≥ 5.0.18 且开启了数据库调试才可能暴露
- TP6 完全删掉了该方法,调用会抛出
Call to undefined method - 所有稳定、可维护的批量删逻辑,都应回归到
destroy()或Db::delete()两条路径
真正麻烦的不是语法,而是软删和硬删混用时的边界判断——比如某个接口既要清理过期数据(硬删),又要归档异常用户(软删),这时候模型配置、调用方式、事务包裹,一个没对齐就丢数据。



















