Laravel怎么实现模型软删除查询开关_Laravel强制包含已删数据【指南】
Laravel软删除以deleted_at时间戳标记记录,默认查询自动过滤。withTrashed()可临时包含已删数据,onlyTrashed()仅查已删。关系查询需显式调用withTrashed(),全局强制开启会破坏软删除语义。
Lara vel 软删除默认排除已删数据,withTrashed()可临时包含,onlyTrashed()仅查已删数据;关系查询需显式调用withTrashed(),全局强制开启会破坏软删除语义。

软删除模型默认不查已删数据,withTrashed() 是开关
Lara vel 的软删除本质上不是“删了”,而是给记录贴上一个 deleted_at 时间戳作为隐藏标记。所以常规查询(get()、find()、包括关系查询)都会被自动加上 WHERE deleted_at IS NULL 这个条件——那些被软删的记录就像直接从业务视野里消失了一样,你根本看不到它们。
想临时把它们拉回来?必须主动调用 withTrashed():
App\Models\Post::withTrashed()->where('id', 123)->first();
这个方法会临时移除 deleted_at IS NULL 的限制,但其他 where 条件照常生效。关键点:它只对当前这一条链式调用有效,不影响后续其他操作。换句话说,这就是一个“单次通行证”。
onlyTrashed() 只查已删数据,别和 withTrashed() 搞混
如果需要专门处理回收站场景(比如列出所有已删除的文章),onlyTrashed() 才是正确的工具。它的作用刚好反过来——把查询条件变成 WHERE deleted_at IS NOT NULL:
App\Models\Post::onlyTrashed()->get(); // 返回所有软删的 Post
withTrashed()= “正常查 + 顺带把已删的也查出来”onlyTrashed()= “只看已删的,未删的完全不关心”- 两个方法互斥,千万不能连用:
withTrashed()->onlyTrashed()会导致不可预知的行为甚至报错 - 它们只影响查询层面,不改变模型本身的
bootSoftDeletes()逻辑
关系查询里漏掉 withTrashed() 就查不到软删关联项
软删除的过滤还会穿透到 Eloquent 关系里。举个例子:一个 User 有多个 Post,其中某个 Post 被软删了:
$user->posts; // 默认查不到那个软删的 Post
要让它可见,有两种方式。第一种是在定义关系时直接声明允许包含已删数据:
public function posts(){
return $this->hasMany(Post::class)->withTrashed();
}
第二种是临时在调用时加上:
$user->posts()->withTrashed()->get();
几点注意事项:
- 关系方法上没写
withTrashed(),就算父模型本身使用了软删除,子模型依然会被过滤掉 withTrashed()必须加在关系构造器返回的 Builder 实例上(即posts()这个方法),不能加在模型实例上- 如果使用了 Eager Loading(比如
with('posts')),同样需要在关系定义里提前声明,否则预加载时照样会丢掉软删项
全局作用域下硬编码 withTrashed() 很危险
有些开发者图省事,想在模型层面一劳永逸地看到所有数据,于是在 boot() 里偷偷塞一个全局作用域强制带上 withTrashed():
protected static function booted(){
static::addGlobalScope('force-with-trashed', function (Builder $builder) {
$builder->withTrashed();
});
}
这种做法非常危险。它会让所有查询——包括后台管理、统计报表、API 接口——统统绕过软删除逻辑,极易导致数据误展示或权限越界。
- 软删除的核心价值是逻辑隔离,不是存储开关;强行全局放开相当于废掉整个机制
- 真正需要“始终可见”的字段(如日志、审计记录),应该单独建表,不走软删除模型
- 如果某些控制器或服务类确实高频用到已删数据,建议封装成专用的 Repository 方法,而不是污染模型的全局行为
软删除从来不是简单的开关,而是一层语义约束。哪条缝该打开、开多久、谁有权开——这些得由调用方自己拿捏,模型只负责守好那道 deleted_at 的门。


































