Laravel Eloquent provides a clean and elegant way to interact with your database. One of its powerful features is Eloquent Scopes, which help you organize and reuse query logic. In this blog, we will dive deep into scopes, their types, and how to use them effectively.
What is a Scope in Laravel?
A scope is a method in an Eloquent model that allows you to encapsulate commonly used query logic. Instead of repeating the same query in multiple places, you define a scope in the model and call it when needed.
Scopes make your code more readable, reusable, and maintainable.
Types of Scopes
Laravel provides two types of scopes:
- Local Scopes
- Global Scopes
1. Local Scopes
Local scopes are used to define query logic that can be reused in your model. They are optional and only applied when you call them.
Defining a Local Scope
To define a local scope, you create a method in your model and prefix it with scope. The convention is:
public function scope[ScopeName](Builder $query, $parameter = null)
Example: Filtering users by status.
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
class User extends Model
{
// Local scope to filter active users
public function scopeActive($query)
{
return $query->where('status', 'active');
}
// Local scope with parameter
public function scopeOfType($query, $type)
{
return $query->where('type', $type);
}
}
Using Local Scopes
Once defined, you can use the scope like this:
use App\Models\User;
// Get all active users
$activeUsers = User::active()->get();
// Get all admin users
$adminUsers = User::ofType('admin')->get();
// Combine scopes
$activeAdmins = User::active()->ofType('admin')->get();
Notice that when calling the scope, you omit the scope prefix. scopeActive becomes active().
2. Global Scopes
Global scopes are applied automatically to every query on the model. They are useful when you want a query to always have certain conditions.
Defining a Global Scope
You can define a global scope in two ways:
- Using a class that implements
Scopeinterface. - Using a closure directly in the model’s
bootedmethod.
Method 1: Using a Scope Class
namespace App\Scopes;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Scope;
class ActiveScope implements Scope
{
public function apply(Builder $builder, Model $model)
{
$builder->where('status', 'active');
}
}
Apply the scope in the model:
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
use App\Scopes\ActiveScope;
class User extends Model
{
protected static function booted()
{
static::addGlobalScope(new ActiveScope);
}
}
Now, all queries on User will only include active users:
$users = User::all(); // Only active users
Method 2: Using a Closure
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
class User extends Model
{
protected static function booted()
{
static::addGlobalScope('active', function ($query) {
$query->where('status', 'active');
});
}
}
Removing a Global Scope
Sometimes, you want to ignore a global scope for a particular query:
use App\Models\User;
// Ignore global scope
$allUsers = User::withoutGlobalScope(ActiveScope::class)->get();
// Or if defined with closure
$allUsers = User::withoutGlobalScope('active')->get();
Why Use Scopes?
Using scopes has several advantages:
- DRY Code: Avoid repeating query logic in multiple places.
- Readability: Makes queries expressive and easier to understand.
- Reusability: Easily use scopes across different parts of your application.
- Maintainability: Changing a scope affects all queries using it.
Best Practices
- Use local scopes for optional filters like
active(),recent(), orofType(). - Use global scopes for mandatory filters like
soft deletedoractive status. - Chain multiple scopes to build complex queries.
- Keep scope names expressive for better readability.
Example: Combining Local and Global Scopes
$recentActiveAdmins = User::active() // Local scope
->ofType('admin') // Local scope
->orderBy('created_at', 'desc')
->get();
Here, the query is clean, readable, and reusable.
Conclusion
Laravel scopes are a powerful feature that allow you to write clean and maintainable query logic. Use local scopes for reusable optional query filters and global scopes for conditions that must always be applied. They help you keep your models organized and make your Eloquent queries more expressive.
