Skip to content

Eloquent Accessors and Mutators — Attribute Transformation in Laravel

DodaTech Updated 2026-06-28 4 min read

In this tutorial, you will learn about Eloquent Accessors and Mutators. We cover key concepts, practical examples, and best practices to help you master this topic.

Laravel Eloquent accessors transform attributes when reading, mutators modify them when writing, and casts handle automatic conversion between database and PHP types.

What You'll Learn

By the end of this tutorial, you'll define accessors and mutators, use attribute casting, create custom cast types, handle date casting, and apply encryption and array casting.

Why Accessors and Mutators Matter

Accessors and mutators keep attribute transformation in the model layer instead of duplicating logic in controllers and views.

Real-World Use

A User model casts password to hashed via mutator, serializes JSON settings via array cast, converts timestamps to Carbon, and uses a custom cast for money values.

Attribute Path

flowchart LR
  A[Eloquent ORM] --> B[Accessors Mutators]
  B --> C[Accessors]
  B --> D[Mutators]
  B --> E[Casts]
  B --> F[Custom Casts]
  B --> G{You Are Here}
  style G fill:#f90,color:#fff

Accessors

Transform attribute values on read.

<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Casts\Attribute;
class User extends Model {
    protected function fullName(): Attribute {
        return Attribute::make(
            get: fn(mixed $value, array $attributes) => trim(
                ($attributes["first_name"] ?? "") . " " . ($attributes["last_name"] ?? "")
            ),
        );
    }
    protected function profileUrl(): Attribute {
        return Attribute::make(
            get: fn() => route("profile", $this->id),
        );
    }
}
// Usage
echo $user->full_name;
echo $user->profile_url;

Mutators

Modify attribute values on write.

<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Casts\Attribute;
use Illuminate\Support\Str;
class Post extends Model {
    protected function title(): Attribute {
        return Attribute::make(
            set: fn(string $value) => [
                "title" => $value,
                "slug" => Str::slug($value),
            ],
        );
    }
    protected function password(): Attribute {
        return Attribute::make(
            set: fn(string $value) => bcrypt($value),
        );
    }
}
// Usage
$post->title = "Hello World"; // Also sets slug to "hello-world"
$user->password = "secret"; // Stores bcrypt hash

Attribute Casting

Cast attributes to common types automatically.

<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
class Product extends Model {
    protected $casts = [
        "price" => "decimal:2",
        "is_active" => "boolean",
        "published_at" => "datetime:Y-m-d",
        "metadata" => "array",
        "config" => "json",
        "options" => "object",
        "encrypted_secret" => "encrypted",
    ];
}
// Usage
$product->published_at instanceof Carbon; // true
$product->is_active; // bool, not int
$product->metadata; // array

Custom Casts

Implement custom casting logic.

<?php
namespace App\Casts;
use Illuminate\Contracts\Database\Eloquent\CastsAttributes;
use Illuminate\Database\Eloquent\Model;
class MoneyCast implements CastsAttributes {
    public function get(Model $model, string $key, mixed $value, array $attributes): string {
        return number_format($value / 100, 2);
    }
    public function set(Model $model, string $key, mixed $value, array $attributes): int {
        return (int) round($value * 100);
    }
}
// In model
protected $casts = ["amount" => MoneyCast::class];

Date Casting

Customize date Serialization with casting.

<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
class Event extends Model {
    protected $casts = [
        "starts_at" => "datetime:Y-m-d H:i",
        "ends_at" => "datetime",
        "registered_at" => "immutable_datetime",
        "processed_at" => "datetime:c",
    ];
    protected $dates = [
        "created_at",
        "updated_at",
        "deleted_at",
    ];
}

Common Mistakes

1. Tight Coupling to Accessors

Accessors should transform values, not perform heavy queries. Keep business logic in services.

2. Forgetting $casts for Security

Non-cast fields can be mass-assigned with unexpected values. Always cast booleans and JSON.

3. Mutators Silently Modifying Input

Mutators that change values without notice cause confusion. Keep mutations predictable.

4. Using Accessors in Queries

Accessors run after fetching. You cannot filter by accessor values in the database query.

5. Over-Casting Performance

Every accessor call runs PHP code. Avoid expensive calculations in accessors being called in loops.

Practice Questions

1. What is the difference between an accessor and a mutator?

Accessors transform on read. Mutators transform on write.

2. How do you cast a JSON column to an array?

Add "column_name" => "array" to the $casts array.

3. What does "encrypted" cast do?

Automatically encrypts on save and decrypts on read using Laravel's encryption.

4. How do you create a custom cast class?

Implement the CastsAttributes interface with get() and set() methods.

5. Challenge: Create a custom slug cast.

<?php
class SlugCast implements CastsAttributes {
    public function get(Model $model, string $key, mixed $value, array $attributes): string {
        return $value;
    }
    public function set(Model $model, string $key, mixed $value, array $attributes): string {
        return Str::slug($value);
    }
}

FAQ

Can I use accessors on non-existent database columns?

Yes. Accessors can return computed values without a database column.

What is the append property for?

Adds accessor values to model serialization (toArray/toJson).

Can I cast to custom enum types?

Yes. Use casts => ['status' => StatusEnum::class] in PHP 8.1+.

Do accessors work on relationships?

No. Accessors work on the model itself. Use API resources for relationships.

What is the performance impact of casting?

Minimal for simple types. Custom casts with heavy logic should be optimized.

Mini Project: Product Model with Casting

Build a product model with comprehensive attribute handling.

<?php
namespace App\Models;
use App\Casts\MoneyCast;
class Product extends Model {
    protected $casts = [
        "price" => MoneyCast::class,
        "compare_price" => MoneyCast::class,
        "is_active" => "boolean",
        "metadata" => "array",
        "published_at" => "datetime",
    ];
    protected function displayName(): Attribute {
        return Attribute::make(get: fn() => sprintf("%s (%s)", $this->name, $this->sku));
    }
}

What's Next

Eloquent Serialization Eloquent Eager Loading Eloquent Query Scopes

Built by the developers of DodaTech

Doda Browser, DodaZIP & Durga Antivirus Pro