Skip to main content

What are scopes?

Eloquent scopes package reusable query constraints so you can apply them anywhere without repeating yourself. There are two kinds.

Local scopes

Defining a local scope

Add the #[Scope] attribute to a protected model method that accepts a Builder instance.
The #[Scope] attribute is in the Illuminate\Database\Eloquent\Attributes namespace. It requires PHP 8.0+.

Using local scopes

Call the scope as a method on the model. You can chain multiple scopes.

Passing parameters

Add extra parameters after the Builder argument.
Pass the value directly when calling the scope.

Combining scopes with orWhere

When you combine scopes with orWhere, use a closure to ensure correct grouping.

Global scopes

How they work

A global scope is a class that implements Illuminate\Database\Eloquent\Scope. The interface requires a single apply method.
Inside apply, you add constraints to the query builder.

Creating a global scope

Generate a scope class with Artisan.
This creates app/Models/Scopes/ActiveScope.php.

Applying a global scope to a model

1

Use the #[ScopedBy] attribute (recommended)

In Laravel 13, the #[ScopedBy] attribute is the simplest approach.
Pass multiple scopes as an array.
2

Register in booted() manually

Override booted and call addGlobalScope.
Once the global scope is applied, User::all() automatically includes WHERE is_active = 1.

Anonymous closure scopes

For simple scopes that do not warrant a separate file, use a named closure.
To remove a closure-based global scope, you must use the string name you provided, not a class name.

Removing global scopes

Inside the framework: SoftDeletingScope

Laravel’s SoftDeletes trait is a good example of how global scopes are used in practice.
withTrashed() simply calls withoutGlobalScope($this) — it removes the SoftDeletingScope itself so soft-deleted records are included.
onlyTrashed() also removes the scope, then adds a whereNotNull('deleted_at') constraint.
The Scope interface does not declare an extend method, but Eloquent’s builder calls it automatically when it exists. Use extend to attach macros to the builder when your scope is applied.

Practical use cases

Multi-tenancy: filtering by tenant automatically

In a SaaS application you want every query to be scoped to the current tenant’s data.
Calling Post::all() now returns only the current tenant’s posts automatically.

Published / draft filtering

Show only published content to visitors, but allow admins to see everything.

Use addSelect instead of select in global scopes

When adding columns inside a global scope, always use addSelect rather than select. Using select overwrites any columns the caller already selected.

Next steps

Eloquent custom casts

Learn how to implement custom casts with the CastsAttributes interface and the Castable pattern.
Last modified on March 28, 2026