Laravel中全局作用域不可删除,只能临时绕过:withoutGlobalScope()精准剔除指定作用域(如ActiveUserScope),withoutGlobalScopes()移除全部(含软删除),withTrashed()语义化查询软删数据,手动where无效,真正禁用需从boot方法中移除注册。

全局作用域不能“删”,只能临时绕过或彻底不注册——它不是数据库里的记录,而是模型启动时注入的查询逻辑。你真正要做的,是控制它何时生效、对谁生效。
withoutGlobalScope() 只剔除指定作用域类
这是最精准的绕过方式,适用于只想跳过某一个自定义作用域(比如 ActiveUserScope),但保留软删除或其他全局约束的场景。
-
withoutGlobalScope()必须传入作用域类的完整命名空间,比如User::withoutGlobalScope(ActiveUserScope::class)->get() - 传错类名或没 use 对应命名空间,会静默失效,查出来的数据仍被过滤
- 它不影响其他全局作用域:如果你同时注册了
TenantScope和ActiveUserScope,只传后者,前者依然生效 - 该方法只对当前链式调用有效,不会污染后续任何查询
withoutGlobalScopes() 会干掉所有,包括软删除
这个方法相当于把模型的“全局过滤开关”全关掉,连 Laravel 自带的 SoftDeletingScope 都会被移除。
-
User::withoutGlobalScopes()->get()返回的结果可能包含deleted_at IS NOT NULL的记录 - 如果你想保留软删除但去掉其他作用域,得显式列出要剔除的:
withoutGlobalScopes([TenantScope::class]) - 注意:它也会清除第三方包添加的全局作用域(比如多语言包、审计包),容易引发权限或数据可见性问题
- 在管理后台或数据导出等可信上下文里用没问题,但绝不能暴露给普通用户请求
软删除作用域不能靠 whereNotNull 绕过
很多人试过加 whereNotNull('deleted_at') 或 orWhereRaw('1=1') 想“覆盖”软删除条件,结果发现还是查不到已删数据——因为全局作用域是在查询构建早期就加进去了,手动 where 是后加的,SQL 里最终变成 WHERE deleted_at IS NULL AND deleted_at IS NOT NULL,永远为 false。
Laravel Cloud(cloud.laravel.com)。此技能用于处理所有 Laravel Cloud 请求,包括搜索和读取数据。只要任务涉及 Laravel Cloud,即使用此技能。
- 正确做法只有三个:
withTrashed()、onlyTrashed()、或withoutGlobalScope(SoftDeletingScope::class) -
withTrashed()是语义最清晰的,它内部就是调用了withoutGlobalScope(SoftDeletingScope::class) - 别在关联查询里混用:比如
User::with('posts')->withTrashed()不会影响 posts 关系的加载逻辑,posts 还是受自己模型的全局作用域控制
真想“删掉”作用域?别注册它
所谓“删除”,其实是回归到注册前的状态。如果你发现某个全局作用域不再需要,最干净的做法是把它从模型的 boot() 或 booted() 方法里删掉。
- 删之前确认所有依赖它的功能(如租户隔离、状态过滤)是否已迁移到别处
- 如果只是临时禁用,别注释掉注册行——注释容易遗忘,上线后引发数据泄露
- Laravel 10+ 要求必须显式注册
SoftDeletingScope,所以删掉它 ≠ 恢复硬删除行为,只是让deleted_at字段彻底失效 - 改完记得清配置缓存:
php artisan config:clear(虽然作用域不走 config,但常和其它配置一起部署)
最容易被忽略的是作用域的嵌套顺序和关联查询的独立性:父模型的作用域不会自动透传到子关系,每个模型的作用域都是各自生效的。这点在调试数据不一致时,往往卡半天才想到。










