软删除批量操作失败主因是deleted_at字段未在$casts中声明为datetime类型、null比较写法错误(应使用wherenotnull而非!= null)、withtrashed()作用域污染后续查询。

软删除字段没加到 $casts 会导致批量操作失败
软删除依赖 deleted_at 字段为 null 或时间戳,但如果模型没声明该字段为日期类型,Laravel 在批量更新时可能把 NULL 当成字符串处理,导致数据库写入失败或条件判断异常。
- 必须在模型中添加:
protected $casts = ['deleted_at' => 'datetime']; - 若用 MySQL,确保字段类型是
DATETIME或TIMESTAMP,不能是VARCHAR - 迁移里推荐用
$table->softDeletes();,它会自动建字段并设为可空
Model::where()->delete() 不触发软删除,而是硬删
delete() 方法本身不区分软删/硬删——只有调用模型实例的 delete() 才走软删除逻辑;直接在查询构造器上链式调用,会绕过模型生命周期,执行 SQL 级别的 DELETE FROM。
- 正确做法:先
get()拿出集合,再遍历调用delete()(适合小批量) - 更高效方式:用
Model::where(...)->update(['deleted_at' => now()]);,手动设时间戳 - 注意:
update()不会触发deleting/deleted事件,需自行处理关联逻辑
restore() 批量恢复要小心 whereRaw 和 null 值判断
恢复软删除记录时,常写 where('deleted_at', '!=', null),但不同数据库对 NULL 的比较行为不一致(MySQL 中 != NULL 永远为 false),实际应改用 whereNotNull('deleted_at')。
- 错误示例:
Model::where('deleted_at', '!=', null)->restore();→ 在 MySQL 下查不到任何记录 - 正确写法:
Model::whereNotNull('deleted_at')->update(['deleted_at' => null]); - 如果要用
restore(),得先withTrashed()拿出数据再调实例方法,否则不生效
withTrashed() 和 withoutTrashed() 影响后续所有 where 条件
这两个作用域不是“一次性开关”,而是修改查询构建器的状态。一旦调用了 withTrashed(),后面所有 where 都会在包含已软删记录的前提下执行,容易造成意料外的数据命中。
- 常见陷阱:写完
Model::withTrashed()->where(...)->get(),接着又想查“未删的”,却忘了重置作用域 - 安全做法:每次需要明确范围时,显式写出完整链式调用,比如
Model::withoutTrashed()->where(...) - 注意:作用域不会自动清理,也不能靠中间变量“保存状态”,别指望赋值给变量后还能复用原始查询对象











