Laravel软删除强制使用deleted_at字段,不可配置;模型必须显式use SoftDeletes;restore()不触发模型事件;查询需用withTrashed()等方法显式控制软删除状态。

软删除字段名必须叫 deleted_at
Laravel 的软删除机制硬编码依赖 deleted_at 字段,不是配置项,不能改。如果你的表用的是 is_deleted 或 deleted_time,SoftDeletes trait 会完全失效——查不到、恢复不了、连 withTrashed() 都没反应。
实操建议:
- 新建迁移时固定用
$table->softDeletes();,它会自动建deleted_at(类型为TIMESTAMP NULL) - 已有表需重命名字段:先加
deleted_at,再把旧字段值转过去(注意 NULL 处理),最后删旧字段 - 别试图通过重写
getDeletedAtColumn()方法绕过——Laravel 8+ 已移除该方法,强行定义会被忽略
use SoftDeletes 必须加在模型里,且不能只加在父类
软删除不是全局开关,是模型级行为。即使你有 BaseModel 继承了 Model,也必须在每个具体模型中显式 use SoftDeletes;,否则 delete() 仍执行物理删除。
常见错误现象:
- 调用
User::find(1)->delete()后数据真的没了,日志里也没触发deleting事件 -
User::withTrashed()->get()返回空集合,但数据库里deleted_at明明有值
原因:模型没 use trait,Laravel 把它当普通模型处理,deleted_at 字段只是个普通字段,不参与任何逻辑。
restore() 不会触发 saving 或 updating 事件
这是 Laravel 软删除的设计事实,不是 bug。调用 $user->restore() 时,底层走的是 update(['deleted_at' => null]),跳过了模型的常规保存生命周期。
所以如果你依赖 saving 事件来自动更新 updated_at 或同步缓存,restore() 后这些逻辑不会执行。
解决方案:
- 手动补发事件:
event(new UserRestored($user));(需自定义事件) - 覆盖
restore()方法,在父类逻辑后追加业务代码 - 改用
forceDelete()+ 重新create()(仅限简单场景,丢失自增 ID 和关联关系)
查询时容易漏掉 withTrashed() 和 onlyTrashed()
默认所有 Eloquent 查询都会自动加 WHERE deleted_at IS NULL,这是隐式行为。这意味着 User::where('name', 'John')->get() 永远不会返回已软删除的记录——哪怕你心里清楚“这里应该包含回收站里的”。
使用场景判断要点:
- 后台列表页要显示「全部/仅启用/已删除」三个 Tab → 分别用
all()、withoutTrashed()、onlyTrashed() - 关联查询默认不包含软删除子记录:
$user->posts不会返回已软删除的Post,需在关系定义里加->withTrashed() - 软删除模型做
belongsTo关联时,若外键指向一个已软删除的父记录,$post->user会返回null(因为关联查询也受软删除限制)
最常被忽略的一点:软删除字段本身可被搜索,但必须显式放开。比如想查「最近7天被删除的用户」,得写 User::onlyTrashed()->whereDate('deleted_at', '>=', now()->subWeek())->get(),而不是靠 whereNotNull('deleted_at') —— 后者绕过了软删除作用域,可能混入其他业务字段为 NULL 的脏数据。









