Query Scopes & Attribute Accessors
Learn how to create reusable query constraints and automatic attribute transformations.
🔍 Query Scopes - Reusable Filters
Without Scope (Repetitive)
Module::where('is_active', true)->get()
Result: 3 modules found
SQL: select * from `modules` where `is_active` = ?
With Scope (Clean & Reusable)
Module::active()->get()
Model Definition:
public function scopeActive($query) {
return $query->where('is_active', true);
}
Benefit: Reusable, readable, chainable
💡 Scope Benefits:
- • DRY Code: Define once, use everywhere
-
•
Chainable: Combine multiple scopes:
Model::active()->recent()->featured() - • Testable: Easy to test in isolation
🎨 Attribute Accessors - Transform on Read
Example Data
Raw Title: Service Container & Dependency Injection
Uppercase (accessor): SERVICE CONTAINER & DEPENDENCY INJECTION
Accessor Definition
protected function titleUppercase(): Attribute {
return Attribute::make(
get: fn () => strtoupper($this->title)
);
}
Usage:
$module->title_uppercase
Common Use Cases:
- • Format names (full_name)
- • Calculate fields (age from birthday)
- • Format currency
- • Parse JSON to arrays
Examples:
- •
$user->full_name - •
$user->age - •
$product->formatted_price
Note:
Accessors don't modify the database, only the retrieved value!
✏️ Attribute Mutators - Transform on Write
Mutator Definition
protected function slug(): Attribute {
return Attribute::make(
set: fn ($value) => Str::slug($value)
);
}
How It Works
Usage:
$module->slug = 'My Title'; // Stored as 'my-title'
Benefit: Automatic data sanitization and formatting
Common Use Cases:
- • Hash passwords
- • Generate slugs
- • Sanitize input
- • Format phone numbers
Examples:
- • Auto-hash passwords
- • Lowercase emails
- • Strip whitespace
- • Convert to proper format
Important:
Mutators transform data BEFORE saving to the database!
📝 Practical Example: User Model
class User extends Model
{
// Accessor: Combine first and last name
protected function fullName(): Attribute
{
return Attribute::make(
get: fn () => "{$this->first_name} {$this->last_name}"
);
}
// Mutator: Always hash passwords
protected function password(): Attribute
{
return Attribute::make(
set: fn ($value) => bcrypt($value)
);
}
// Both: Normalize email
protected function email(): Attribute
{
return Attribute::make(
get: fn ($value) => strtolower($value),
set: fn ($value) => strtolower(trim($value))
);
}
// Query Scope: Only active users
public function scopeActive($query)
{
return $query->where('is_active', true);
}
// Query Scope: Recently registered
public function scopeRecent($query)
{
return $query->where('created_at', '>=', now()->subDays(30));
}
}
// Usage:
$user = User::active()->recent()->first();
echo $user->full_name; // "John Doe" (accessor)
$user->password = 'secret123'; // Automatically hashed (mutator)
$user->email = ' TEST@EXAMPLE.COM '; // Stored as "test@example.com" (mutator)