Advanced Laravel Eloquent: Scopes, Accessors, Mutators
Leverage advanced Eloquent features for clean code.
Laravel's Eloquent ORM is famously expressive, helping developers manage database records efficiently. While basic CRUD operations are straightforward, mastering Eloquent's scopes, accessors, and mutators can elevate your codebase to a whole new level of clarity and maintainability. In this article, we'll dive deep into these advanced features, illustrating how they can streamline your development process.
Table of Contents
- Understanding Eloquent Scopes
- Accessors: Transforming Model Attributes
- Mutators: Modifying Data Before Persistence
- Best Practices and Real-World Examples
- Conclusion
Understanding Eloquent Scopes
Eloquent scopes allow you to encapsulate commonly used query logic, leading to more readable and reusable code.
Global Scopes
Global scopes automatically apply constraints to all queries involving your model. They are useful for "soft deletes," multi-tenancy, or record status filtering.
How to Create a Global Scope
Create a class implementing Illuminate\Database\Eloquent\Scope
and add your logic to the apply
method:
namespace App\Scopes;
use Illuminate\Database\Eloquent\Scope;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Builder;
class ActiveScope implements Scope
{
public function apply(Builder $builder, Model $model)
{
$builder->where('is_active', 1);
}
}
Register it in your model’s booted
method:
protected static function booted()
{
static::addGlobalScope(new \App\Scopes\ActiveScope);
}
Now every query on this model will only include active records.
Local Scopes
Local scopes let you group query constraints and call them as methods.
Defining a Local Scope
// In your Eloquent model
public function scopePopular($query)
{
return $query->where('views', '>', 100);
}
Using Local Scopes
$popularPosts = Post::popular()->get();
Local scopes can even accept parameters:
public function scopeOfType($query, $type)
{
return $query->where('type', $type);
}
$newsPosts = Post::ofType('news')->get();
Accessors: Transforming Model Attributes
Accessors let you format Eloquent model properties when retrieving them. This keeps your presentation logic separate from your controllers or views.
Creating an Accessor
Define a method as get{AttributeName}Attribute
:
public function getFullNameAttribute()
{
return "{$this->first_name} {$this->last_name}";
}
Now you can access $user->full_name
as if it were a property, even though it doesn’t exist in the database!
Example: Formatting Dates
public function getCreatedAtAttribute($value)
{
return \Carbon\Carbon::parse($value)->diffForHumans();
}
Now, $model->created_at
gives you a human-readable date.
Mutators: Modifying Data Before Persistence
Mutators are called when setting an attribute’s value on the model, allowing you to modify data before it’s saved to the database.
Creating a Mutator
Define a method as set{AttributeName}Attribute
:
public function setPasswordAttribute($value)
{
$this->attributes['password'] = bcrypt($value);
}
Now, when you assign $user->password = 'secret';
, it is hashed automatically.
Casting and Mutators: A Powerful Combo
You can combine mutators with Eloquent’s $casts
property for type conversion:
protected $casts = [
'metadata' => 'array',
];
Best Practices and Real-World Examples
1. Encapsulate Business Logic
Leverage accessors and mutators to keep controllers clean:
// Accessor for formatted price
public function getFormattedPriceAttribute()
{
return '$' . number_format($this->price / 100, 2);
}
2. Reusable Query Logic
Use local scopes for common filters:
// scope
public function scopeActive($query)
{
return $query->where('status', 'active');
}
// Usage
User::active()->get();
3. Secure Sensitive Attributes
Mutate attributes like passwords, or normalize emails before storage to enforce consistency:
public function setEmailAttribute($value)
{
$this->attributes['email'] = strtolower($value);
}
Conclusion
By leveraging scopes, accessors, and mutators in Laravel Eloquent, you can write expressive, reusable, and maintainable code. These features help remove clutter from your controllers, keep business logic close to your data, and ensure concise query definitions and attribute transformations.
Ready to take your Eloquent skills to the next level? Start refactoring your models with these techniques and see how clean and efficient your code can become.
Have questions or want to share your experiences using these features? Drop a comment below!