Introduction

Attribute casting converts raw database values to native PHP types automatically — dates to Carbon instances, JSON to arrays, integers to enums — so you don't have to remember to call json_decode every time.

Key Concepts

  • casts() method: The modern Laravel 11+ way to declare casts. Returns an associative array of attribute => cast type.
  • Primitive casts: boolean, integer, float, double, decimal:2, string.
  • Date casts: datetime, date, immutable_datetime, datetime:Y-m-d.
  • Array / JSON casts: array, json, object, collection.
  • Enum cast: Cast to a PHP enum class, giving you type-safe comparisons.
  • Custom cast class: A class implementing CastsAttributes for complex transformations (value objects, encrypted blobs).

Real World Context

Instead of calling json_decode($user->settings) in every controller, declare the cast once and Laravel handles it everywhere. Same for dates, booleans, and enums. This is how you get a consistent, type-safe surface on top of a relational database.

Deep Dive

Attribute casting automatically converts database values to common PHP data types. Instead of manually casting values everywhere, define casts once on your model.

Basic Casting

php
<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Model;

class User extends Model
{
    /**
     * Get the attributes that should be cast.
     */
    protected function casts(): array
    {
        return [
            'email_verified_at' => 'datetime',
            'is_admin' => 'boolean',
            'settings' => 'array',
            'birthday' => 'date',
            'balance' => 'decimal:2',
        ];
    }
}

Available Cast Types

Primitive Types

php
protected function casts(): array
{
    return [
        'is_active' => 'boolean',      // true/false
        'age' => 'integer',             // int
        'price' => 'float',             // float ('double' is an alias)
        'amount' => 'decimal:2',        // string with 2 decimals — preferred for money
        'data' => 'string',             // string
    ];
}

Date/Time Types

php
protected function casts(): array
{
    return [
        'created_at' => 'datetime',           // Carbon instance
        'published_at' => 'datetime:Y-m-d',   // Custom format
        'birthday' => 'date',                 // Carbon (date only)
        'time_slot' => 'timestamp',           // Unix timestamp
        'expires_at' => 'immutable_datetime', // CarbonImmutable
    ];
}

Usage:

php
$user = User::find(1);

$user->birthday;                    // Carbon instance
$user->birthday->age;               // 25
$user->birthday->format('F j, Y');  // "January 15, 1999"
$user->birthday->diffForHumans();   // "25 years ago"

Array and JSON

php
use Illuminate\Database\Eloquent\Casts\AsCollection;

protected function casts(): array
{
    return [
        'settings' => 'array',             // JSON to PHP array
        'preferences' => 'json',           // Same as array
        'metadata' => 'object',            // JSON to stdClass
        'collection' => 'collection',      // JSON to Collection
        'options' => AsCollection::class,  // Same, via the dedicated cast class
    ];
}

Usage:

php
// In database: {"theme":"dark","notifications":true}

$user = User::find(1);

// As array
$user->settings;                    // ['theme' => 'dark', 'notifications' => true]
$user->settings['theme'];           // 'dark'

// Update
$user->settings = ['theme' => 'light', 'notifications' => false];
$user->save();  // Saved as JSON string

// Merge values
$settings = $user->settings;
$settings['language'] = 'en';
$user->settings = $settings;
$user->save();

Encrypted Casting

php
protected function casts(): array
{
    return [
        'secret' => 'encrypted',              // Encrypted string
        'api_keys' => 'encrypted:array',      // Encrypted array
        'token' => 'encrypted:collection',    // Encrypted collection
        'credentials' => 'encrypted:object',  // Encrypted object
    ];
}

Data is encrypted at rest:

php
$user->secret = 'my-api-key';  // Encrypted when saved
$user->secret;                  // Decrypted when accessed: "my-api-key"

Hashed Casting (Laravel 10+)

php
protected function casts(): array
{
    return [
        'password' => 'hashed',
    ];
}
php
$user->password = 'secret';  // Automatically hashed!
// No need for Hash::make()

Enum Casting

Cast to PHP enums:

php
// app/Enums/UserStatus.php
enum UserStatus: string
{
    case Pending = 'pending';
    case Active = 'active';
    case Suspended = 'suspended';
}
php
// In User model
protected function casts(): array
{
    return [
        'status' => UserStatus::class,
    ];
}

Usage:

php
$user = User::find(1);

$user->status;                           // UserStatus::Active
$user->status === UserStatus::Active;    // true
$user->status->value;                    // "active"
$user->status->name;                     // "Active"

// Set using enum
$user->status = UserStatus::Suspended;

// Query
User::where('status', UserStatus::Active)->get();

Custom Cast Classes

For complex casting logic:

For complex casting logic, you define two classes: a value object (what callers interact with) and a cast class (the bridge between the database column and the value object).

bash
php artisan make:cast MoneyCast
php
<?php

// app/ValueObjects/Money.php — the plain value object
namespace App\ValueObjects;

final class Money
{
    public function __construct(
        public readonly int $cents,
        public readonly string $currency = 'USD',
    ) {}

    public function format(): string
    {
        return sprintf('%.2f %s', $this->cents / 100, $this->currency);
    }
}
php
<?php

// app/Casts/MoneyCast.php — the cast that bridges DB <-> Money
namespace App\Casts;

use App\ValueObjects\Money;
use Illuminate\Contracts\Database\Eloquent\CastsAttributes;
use Illuminate\Database\Eloquent\Model;

class MoneyCast implements CastsAttributes
{
    public function __construct(
        protected string $currency = 'USD',
    ) {}

    /** Cast the given value (from database to PHP). */
    public function get(Model $model, string $key, mixed $value, array $attributes): Money
    {
        return new Money((int) $value, $this->currency);
    }

    /** Prepare the given value for storage (from PHP to database). */
    public function set(Model $model, string $key, mixed $value, array $attributes): int
    {
        return $value instanceof Money ? $value->cents : (int) $value;
    }
}

Usage on the model:

php
use App\Casts\MoneyCast;

protected function casts(): array
{
    return [
        'price' => MoneyCast::class . ':USD',
        'cost'  => MoneyCast::class . ':EUR',
    ];
}

// In the app:
$product->price;            // Money instance
$product->price->format();  // "19.99 USD"
$product->price = new Money(2500);  // $25.00, stored as 2500 cents

Castable Classes

Value objects that handle their own casting:

php
use Illuminate\Contracts\Database\Eloquent\Castable;

class Address implements Castable
{
    public static function castUsing(array $arguments): string
    {
        return AddressCast::class;
    }
}
php
protected function casts(): array
{
    return [
        'address' => Address::class,
    ];
}

// Usage
$user->address = new Address('123 Main St', 'New York', 'NY');
$user->address->city;  // "New York"

Common Pitfalls

  1. Forgetting to cast JSON columns — Without a cast, $user->settings returns a string that you must manually decode.
  2. Storing dates as strings — Without a datetime cast, comparisons and arithmetic don't work.
  3. Not using enum casts for fixed value sets — You lose type safety and get stuck with stringly-typed comparisons.

Best Practices

  1. Cast every JSON/array column — One cast, automatic decode and encode.
  2. Use enum casts over string matching — Type-safe, self-documenting.
  3. Write custom cast classes for value objects — Money, Address, Coordinates — keep them as first-class types.

Summary

  • Declare casts in the casts() method on the model.
  • Primitive, date, array/JSON, encrypted, and enum casts cover most needs.
  • Custom cast classes (CastsAttributes interface) handle value objects.
  • Castable classes let value objects declare their own cast.
  • Casts run automatically on read and write — no manual serialization.
✓ Completed