Scopes in Laravel are a powerful feature that allows you to encapsulate query constraints into reusable methods. They help keep your code DRY (Don’t Repeat Yourself) and make your queries more readable.
Table of Contents
- Types of Scopes
- Local Scopes
- Global Scopes
- Dynamic Scopes
- Scope Features
- Best Practices
1. Types of Scopes
There are two main types of scopes in Laravel:
- Local Scopes: Apply to specific queries when called.
- Global Scopes: Apply to all queries for a model.
2. Local Scopes
Local scopes allow you to define common sets of query constraints that you may easily re-use.
Basic Example
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Attributes\Scope;
class User extends Model
{
/**
* Scope a query to only include active users.
*/
#[Scope]
public function active(Builder $query)
{
return $query->where('active', 1);
}
/**
* Scope a query to only include popular users.
*/
#[Scope]
public function popular(Builder $query)
{
return $query->where('votes', '>', 10);
}
}
Using Local Scopes
// Get all active users
$activeUsers = User::active()->get();
// Get all popular users
$popularUsers = User::popular()->get();
// Chain multiple scopes
$users = User::active()->popular()->orderBy('name')->get();
/**
* Combining multiple Eloquent model scopes via an or query operator
* may require the use of closures to achieve the correct logical grouping:
*/
$users = User::active()->orWhere(function (Builder $query) {
$query->popular();
})->get();
/**
* Laravel offers a higher-order orWhere method that enables
* fluent scope chaining without requiring closure wrappers.
*/
$users = User::active()->orWhere->popular()->get();
3. Global Scopes
Global scopes allow you to add constraints to all queries for a given model.
Creating a Global Scope:
You can generate a new global scope using the Artisan command php artisan make:scope ScopeName. This will automatically create the scope class in your application's app/Models/Scopes directory.
php artisan make:scope AgeScope
The apply method can add WHERE clauses or other query constraints to modify the query builder instance:
<?php
namespace App\Models\Scopes;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Scope;
class AgeScope implements Scope
{
/**
* Apply the scope to a given Eloquent query builder.
*/
public function apply(Builder $builder, Model $model): void
{
$builder->where('age', '>', 18);
}
}
Apply the scope to a model:
<?php
namespace App\Models;
use App\Models\Scopes\AgeScope;
use Illuminate\Database\Eloquent\Attributes\ScopedBy;
#[ScopedBy([AgeScope::class])]
class User extends Model
{
//
}
Alternatively, you can manually register a global scope by overriding the model’s booted() method and calling the addGlobalScope() method:
<?php
namespace App\Models;
use App\Models\Scopes\AgeScope;
use Illuminate\Database\Eloquent\Model;
class User extends Model
{
/**
* The "booted" method of the model.
*/
protected static function booted(): void
{
static::addGlobalScope(new AgeScope);
}
}
Now all queries for the User model will automatically include the age constraint.
Anonymous Global Scopes
You can also define global scopes using closures:
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Model;
class User extends Model
{
/**
* The "booted" method of the model.
*/
protected static function booted(): void
{
static::addGlobalScope('age', function (Builder $builder) {
$builder->where('age', '>', 18);
});
}
}
Removing Global Scopes
/** To remove a global scope for a specific query,
* use the withoutGlobalScope() method:
*/
User::withoutGlobalScope(AgeScope::class)->get();
// Alternatively, if you defined the global scope using a closure
User::withoutGlobalScope('age')->get();
// Remove all global scopes
User::withoutGlobalScopes()->get();
// Remove specific global scopes
User::withoutGlobalScopes([
AgeScope::class
])->get();
4. Dynamic Scopes
You can create scopes that accept parameters:
class User extends Model
{
/**
* Scope a query to only include users of a given type.
*/
public function scopeOfType(Builder $query, string $type): void
{
$query->where('type', $type);
}
}
// Usage
$admins = User::ofType('admin')->get();
5. Scope Features
Chaining Scopes
$users = User::active()
->ofType('admin')
->orderBy('name')
->get();
Combining with Other Query Builder Methods
$users = User::active()
->where('created_at', '>', now()->subDays(30))
->with('posts')
->paginate(10);
Conditional Scopes
#[Scope]
public function activeOrAdmin($query, $isAdmin)
{
return $query->when(!$isAdmin, function ($query) {
return $query->active();
});
}
6. Best Practices
- Keep scopes focused: Each scope should do one specific thing
- Document your scopes: Use PHPDoc to explain what each scope does
- Consider performance: Complex scopes can impact query performance
- Use dynamic scopes judiciously: They’re powerful but can make code harder to understand if overused
Scopes are a powerful tool in Laravel that can significantly improve the organization and readability of your database queries while reducing code duplication.
Free resources for this topic
Code snippets and fixes for this topic
Get one practical tip every week
New tutorials, fixes and free cheat sheets from DEV Talk, straight to your inbox. No spam, unsubscribe in one click.
Free download: Laravel interview questions (PDF)
Have a question about this post?
Ask the community: other developers and the author can answer.
Get notified about new articles
One click, no email needed. Turn it off anytime with the bell at the top.
Get new articles on your iPhone
Add DEV Talk to your Home Screen, open it, then tap the bell.
Enjoying the content?
If this article helped you, you can support the author with a coffee. It keeps new posts coming.
Comments 0
No comments yet
Be the first to share your thoughts.