Skip to main content
Create your own
Lesson illustration

Eloquent Accessors, Mutators, and Attribute Casting

Hello! Welcome to the first lesson in our "Advanced Eloquent ORM" module.

In the previous module, we successfully built and seeded our database. We now have tables full of realistic test data for our Product, Category, User, and Order models. This data is stored in its raw format, exactly as we defined it in our migrations—prices as numbers, names as strings, and flags as integers.

This lesson is about making our models smarter. We'll learn how to transform this raw data automatically as it enters or leaves our application. Our goal is to apply accessors, mutators, and attribute casting to transform model data. By the end of this lesson, you'll be able to:

  • Format data for display without cluttering your views or controllers.
  • Modify data before it's saved to the database to ensure consistency.
  • Automatically convert data between database-friendly and application-friendly types.
  • Encapsulate this transformation logic directly within your Eloquent models, following the principle of "fat models, thin controllers."

Let's begin by understanding these three powerful concepts.

1. The Trifecta of Attribute Transformation

At its core, Eloquent provides three primary mechanisms for manipulating attribute data: Accessors, Mutators, and Casting.

What are Accessors, Mutators, and Casting in Laravel?
This diagram gives a high-level overview. **Accessors** get data, **Mutators** set data, and **Casting** handles type conversion for both operations.
  • Accessors transform an attribute's value when you retrieve it from a model. Think of them as "getters." For example, retrieving a price of 1999 from the database but having your model present it as $19.99.
  • Mutators transform an attribute's value before it's saved to the database. Think of them as "setters." For example, a user enters "cool new product" for a name, and the mutator automatically saves it as "Cool New Product".
  • Attribute Casting is a more declarative, and often simpler, alternative for converting attributes to common data types. For example, automatically converting a 1 or 0 from the database into a true or false boolean in your application.

Let's explore how to implement each of these.

2. Accessors and Mutators

There are two syntaxes for defining accessors and mutators. We'll look at the "classic" way first, as you may see it in older projects, and then dive into the modern, recommended approach.

The "Classic" Syntax

The traditional way involves defining specific methods in your model with get...Attribute and set...Attribute naming conventions.

Laravel Basics - Accessors, Mutators and Casting

This video from Laratips provides a clear, practical demonstration of the classic syntax. It shows how to build an accessor to format a product title and a mutator to convert a price into cents for storage.

Watch the sections on accessors (03:00 - 07:02) and mutators (07:02 - 11:14). Pay close attention to the method naming convention: getTitleAttribute and setPriceAttribute.

As you saw, the logic is straightforward, but it requires separate methods for getting and setting, which can lead to duplicated logic if the transformations are related (like converting currency).

The Modern, Unified Syntax

Modern Laravel versions (8+) introduced a cleaner, more powerful way to define accessors and mutators using a single method that returns an Attribute object. This is the recommended approach.

This new syntax consolidates the get and set logic for a single attribute into one place, making your models easier to read and maintain.

The New Way to Define Eloquent Accessors and Mutators

This Laracasts video is an excellent guide to the modern syntax. It clearly explains how to refactor from the old style to the new, unified Attribute class.

Watch the entire video (00:28 - 07:54). It's short and packed with information. Focus on how a single method can now define both a get and a set closure, and how to use the Attribute::get() and Attribute::set() helpers when you only need one or the other.

For further detail and code examples, the official Laravel documentation is the best resource.

Eloquent: Mutators & Casting - Defining Accessors and Mutators

Now, let's solidify your understanding by reviewing the official documentation. This will reinforce the concepts from the video with precise explanations and code samples.

Read the sections 'Defining an Accessor' and 'Defining a Mutator'. Notice how named arguments (get: and set:) make the code highly readable.

Your Turn: Implementing Accessors & Mutators

Let's apply this to our Product model.

  1. Create a formatted_price accessor: Our database stores prices like 29.99. Let's create an accessor for a new virtual attribute formatted_price that returns this as a string prefixed with a dollar sign (e.g., "$29.99").

    In your app/Models/Product.php model, add the following method:

    <?php
    
    namespace App\Models;
    
    use Illuminate\Database\Eloquent\Casts\Attribute; // Import the class
    use Illuminate\Database\Eloquent\Factories\HasFactory;
    use Illuminate\Database\Eloquent\Model;
    
    class Product extends Model
    {
        use HasFactory;
    
        // ... other properties and methods
    
        /**
         * Get the product's formatted price.
         */
        protected function formattedPrice(): Attribute
        {
            return Attribute::make(
                get: fn () => '$' . number_format($this->price, 2),
            );
        }
    }
    

    Now, if you retrieve a product, you can access $product->formatted_price to get the formatted string.

  2. Create a name mutator: Let's ensure every product name is stored in the database with the first letter of each word capitalized.

    In the same Product.php model, add another method:

    <?php
    
    namespace App\Models;
    
    use Illuminate\Database\Eloquent\Casts\Attribute;
    use Illuminate\Support\Str; // Import the Str helper
    // ...
    
    class Product extends Model
    {
        // ...
    
        /**
         * Interact with the product's name.
         */
        protected function name(): Attribute
        {
            return Attribute::make(
                set: fn (string $value) => Str::title($value),
            );
        }
    
        // ... formattedPrice method
    }
    

    Now, whenever you set a product's name (e.g., $product->name = 'a new gadget'; $product->save();), it will automatically be converted to "A New Gadget" before being stored.

3. Attribute Casting

For many common data transformations, writing a full accessor/mutator is overkill. Attribute casting provides a simple, declarative way to convert attribute types. You simply define a casts property on your model.

Laravel Basics - Accessors, Mutators and Casting

Let's revisit the Laratips video to see a perfect example of attribute casting. This clip shows how to convert a database integer (0 or 1) into a true boolean value in your application.

Watch from 11:05 to 13:52. Notice how much simpler adding 'is_active' => 'boolean' to the $casts array is compared to writing a full getIsActiveAttribute method.

Eloquent supports a wide variety of cast types.

Eloquent: Mutators & Casting - Attribute Casting

The official documentation provides a comprehensive list of all built-in cast types. It's a great reference to have bookmarked.

Skim through the 'Attribute Casting' section. Pay special attention to the list of supported types like boolean, integer, datetime, array, and collection.

Your Turn: Applying Casts

Let's add a couple of casts to our models.

  1. Boolean Cast: First, create a new migration to add an is_active column to your products table. Since you're using MSSQL, a BIT type is appropriate.

    php artisan make:migration add_is_active_to_products_table --table=products
    

    In the generated migration file:

    public function up(): void
    {
        Schema::table('products', function (Blueprint $table) {
            $table->boolean('is_active')->default(true); // In MSSQL, this creates a BIT column
        });
    }
    

    Run the migration: php artisan migrate.

    Now, in your Product.php model, add the casts property (or casts() method for Laravel 10+):

    // In app/Models/Product.php
    class Product extends Model
    {
        // ...
        /**
         * The attributes that should be cast.
         *
         * @var array
         */
        protected $casts = [
            'is_active' => 'boolean',
        ];
        // ...
    }
    

    Now, when you access $product->is_active, you will get a true PHP boolean (true/false) instead of 1/0.

  2. Array Cast: Add a properties column to your products table to store extra JSON data.

    php artisan make:migration add_properties_to_products_table --table=products
    

    In the migration:

    public function up(): void
    {
        Schema::table('products', function (Blueprint $table) {
            $table->json('properties')->nullable();
        });
    }
    

    Run php artisan migrate.

    Update your casts array in Product.php:

    protected $casts = [
        'is_active' => 'boolean',
        'properties' => 'array', // or 'AsCollection' for a fluent collection object
    ];
    

    Now you can set $product->properties = ['color' => 'blue', 'size' => 'large']; and Laravel will automatically encode it to a JSON string for storage. When you retrieve it via $product->properties, it will be automatically decoded back into a PHP array.

4. Custom Casts: Reusable Transformation Logic

What happens when your transformation logic is complex or you want to reuse it across multiple models? This is where Custom Casts shine. A custom cast is a dedicated class that handles the get and set logic for an attribute.

A great use case is handling money. Storing currency as integers (cents) is a best practice to avoid floating-point precision issues, but you want to work with them as dollars in your application. A custom MoneyCast can handle this conversion automatically.

Laravel Basics - Accessors, Mutators and Casting

The Laratips video concludes with an excellent demonstration of creating a custom MoneyCast class to abstract the price conversion logic we saw earlier.

Watch from 13:52 to 19:31. Follow the steps: create a Casts directory, define a class that implements CastsAttributes, implement the get and set methods, and apply the cast in your model.

This approach is incredibly powerful for creating expressive, domain-specific objects that are seamlessly persisted to the database.

Conclusion

You've now mastered the key techniques for transforming Eloquent model data. This allows you to keep your data layer clean and consistent, while making your models more intelligent and expressive.

Key Takeaways:

  • Accessors (get:) transform data when it's read from the model.
  • Mutators (set:) transform data before it's written to the database.
  • The modern Attribute class provides a unified, clean syntax for defining accessors and mutators.
  • Attribute Casting is a simple, declarative way to handle common type conversions (e.g., boolean, array, datetime).
  • Custom Casts are reusable classes that encapsulate complex transformation logic, promoting a clean, DRY (Don't Repeat Yourself) codebase.

Next Up:

We've learned to transform individual attributes on our models. In the next lesson, we will move on to transforming and constraining queries. You'll learn how to implement local and global query scopes to create reusable query constraints, making it easy to apply common filters (like where('is_active', true)) across your entire application.

Can't find a good explanation? Sign up and we'll make it for you

Sign up