Accessors & Mutators

Modifying and accessing attributes with Accessors and Mutators

graph LR A[Read Attribute] --> B{Has Accessor?} B -->|Yes| C[Execute Accessor] B -->|No| D[Return Raw Value] C --> E[Transformed Value] F[Write Attribute] --> G{Has Mutator?} G -->|Yes| H[Execute Mutator] G -->|No| I[Store Raw Value] H --> J[Transformed Value to DB]

Accessors and Mutators allow you to transform model attributes before reading or writing. This feature allows you to keep data transformation logic in the model.


Accessors:


Accessors allow you to transform attributes before reading. Method name must be in format get{Attribute}Attribute and attribute name must be camelCase.


  • Executed before reading attribute
  • Method name: <code>get{Attribute}Attribute</code>
  • Can use other attributes
  • Can perform calculations
  • Can format data

Mutators:


Mutators allow you to transform attributes before writing. Method name must be in format set{Attribute}Attribute.


  • Executed before writing attribute
  • Method name: <code>set{Attribute}Attribute</code>
  • Can perform validation
  • Can transform data
  • Can perform encryption/hashing

Benefits:


  • <strong>Data Transformation</strong>: Transform data in one place
  • <strong>Consistency</strong>: Ensure consistency in data transformation
  • <strong>Encapsulation</strong>: Keep transformation logic in model
  • <strong>Reusability</strong>: Reuse transformation logic
  • <strong>Type Safety</strong>: Automatic type conversion

Examples

Simple Accessor

<?php

class User extends Model
{
    public function getFullNameAttribute()
    {
        return "{$this->first_name} {$this->last_name}";
    }
    
    public function getIsAdminAttribute()
    {
        return $this->role === 'admin';
    }
}

// Usage
$user = User::find(1);
echo $user->full_name; // Accessor
echo $user->is_admin; // Accessor

Simple accessors for combining attributes and calculations.

Simple Mutator

<?php

class User extends Model
{
    public function setPasswordAttribute($value)
    {
        $this->attributes['password'] = Hash::make($value);
    }
    
    public function setEmailAttribute($value)
    {
        $this->attributes['email'] = strtolower(trim($value));
    }
}

// Usage
$user = new User();
$user->password = 'secret'; // Automatically hashed
$user->email = '  JOHN@EXAMPLE.COM  '; // Automatically lowercased and trimmed

Simple mutators for hashing and normalizing data.

Accessor with Formatting

<?php

class Product extends Model
{
    public function getFormattedPriceAttribute()
    {
        return '$' . number_format($this->price, 2);
    }
    
    public function getCreatedAtFormattedAttribute()
    {
        return $this->created_at->format('Y-m-d H:i:s');
    }
    
    public function getStatusBadgeAttribute()
    {
        return match($this->status) {
            'active' => '<span class="badge badge-success">Active</span>',
            'inactive' => '<span class="badge badge-danger">Inactive</span>',
            default => '<span class="badge badge-secondary">Unknown</span>'
        };
    }
}

Accessors for formatting data for display.

Mutator with Validation

<?php

class User extends Model
{
    public function setPhoneAttribute($value)
    {
        // Remove non-numeric characters
        $cleaned = preg_replace('/[^0-9]/', '', $value);
        
        // Validate phone number
        if (strlen($cleaned) < 10) {
            throw new \InvalidArgumentException('Invalid phone number');
        }
        
        $this->attributes['phone'] = $cleaned;
    }
    
    public function setCreditCardAttribute($value)
    {
        // Encrypt sensitive data
        $this->attributes['credit_card'] = encrypt($value);
    }
}

Mutators with validation and encryption for sensitive data.

Accessor with Relationships

<?php

class Order extends Model
{
    public function getTotalItemsAttribute()
    {
        return $this->items()->sum('quantity');
    }
    
    public function getTotalAmountAttribute()
    {
        return $this->items()->sum(DB::raw('quantity * price'));
    }
    
    public function getIsCompleteAttribute()
    {
        return $this->status === 'completed' && $this->items()->count() > 0;
    }
}

Accessors that use relationships.

Appending Accessors

<?php

class User extends Model
{
    protected $appends = ['full_name', 'is_admin'];
    
    public function getFullNameAttribute()
    {
        return "{$this->first_name} {$this->last_name}";
    }
    
    public function getIsAdminAttribute()
    {
        return $this->role === 'admin';
    }
}

// Accessors are automatically included in JSON/Array
$user = User::find(1);
$user->toArray(); // Includes full_name and is_admin
$user->toJson(); // Includes full_name and is_admin

Using $appends to automatically include accessors in JSON/Array.

Conditional Mutators

<?php

class User extends Model
{
    public function setPasswordAttribute($value)
    {
        // Only hash if value is not already hashed
        if (!empty($value) && !Hash::needsRehash($value)) {
            $this->attributes['password'] = $value;
        } else {
            $this->attributes['password'] = Hash::make($value);
        }
    }
    
    public function setSlugAttribute($value)
    {
        // Generate slug if not provided
        if (empty($value) && !empty($this->attributes['title'])) {
            $this->attributes['slug'] = Str::slug($this->attributes['title']);
        } else {
            $this->attributes['slug'] = Str::slug($value);
        }
    }
}

Mutators with conditional logic for smart transformations.

Use Cases

  • Combining multiple attributes into one attribute
  • Formatting data for display
  • Hashing and encrypting sensitive data
  • Normalizing and cleaning user input
  • Calculations based on attributes
  • Type conversion and casting

Common Mistakes

  • Forgetting camelCase in accessor/mutator names
  • Using accessor for complex business logic
  • Mutator with side effects causing issues
  • Forgetting return statement in accessor
  • Using accessor for data that should be stored in database
  • Accessor with expensive operations affecting performance

Best Practices

  • Use accessors for simple transformations
  • Use mutators for data normalization
  • Use $appends to automatically include accessors
  • Keep accessor logic simple
  • Use mutators for security-sensitive operations
  • Keep accessor names descriptive
  • Use caching for expensive accessors

Edge Cases

  • Accessors with circular dependencies
  • Mutators with validation errors
  • Accessors in JSON serialization
  • Mutators with mass assignment
  • Accessors with relationships that may cause N+1
  • Mutators with complex conditional logic

Performance Notes

  • Accessors overhead is very low
  • Avoid expensive operations in accessors
  • Use caching for expensive accessors
  • Mutators only execute on write
  • Use $appends with caution as they execute on every serialization

Security Notes

  • Use mutators for hashing passwords
  • Use mutators for encrypting sensitive data
  • Ensure accessors don't expose sensitive data
  • Use validation in mutators
  • Ensure accessors don't have SQL injection

Interview Points

  • What are Accessor and Mutator and what is the difference?
  • How can you include accessor in JSON?
  • When should you use accessor and when mutator?
  • How can you use accessor with relationships?
  • What is the difference between accessor and attribute casting?
  • How can you define conditional mutator?

Version Notes

  • Laravel 11.x: Improved performance in accessor/mutator execution
  • Laravel 11.x: Better support for attribute casting
  • Laravel 10.x: Improved $appends functionality
  • Laravel 9.x: Improved accessor caching