Cast — automatic type transformation when reading or writing a model attribute
Concept
Cast — a declarative instruction telling Eloquent how to automatically convert an attribute's raw database value to a PHP type (and back).
The problem casts solve: Databases store everything as strings or integers. A boolean stored as 1 in MySQL should be true in PHP. A JSON string in a TEXT column should be an array. Without casts, you'd manually convert every time you read or write — tedious and error-prone.
Built-in cast types:
'integer'/'int': String → int.'float'/'double'/'real': String → float.'boolean'/'bool':'1'/'0'→true/false.'string': Cast to string.'array'/'json': JSON string ↔ PHP array. Serializes on save, deserializes on read.'collection': JSON string ↔ Laravel Collection.'datetime'/'date'/'timestamp': String → Carbon instance (with optional format).'decimal:2': Float with 2 decimal places.'encrypted': Encrypted at rest, decrypted on read. Uses Laravel's encryption.'hashed': Auto-hashes on assignment (PHP 8.1+). Like a built-in password mutator.
Custom casts: Implement \Illuminate\Contracts\Database\Eloquent\CastsAttributes — define get() and set() methods. Cast a column to a Value Object (e.g., Money, Email).
Enum casts (PHP 8.1+): 'status' => OrderStatus::class — stores enum ->value in DB, returns enum instance on read.
Difference from accessor/mutator: Casts are declarative and type-focused. Accessors are imperative and can contain any logic.
Code Example
<?php
use Illuminate\Database\Eloquent\Model;
class User extends Model
{
protected $casts = [
'active' => 'boolean', // DB: 1/0 → PHP: true/false
'age' => 'integer', // DB: '25' → PHP: 25
'score' => 'float',
'settings' => 'array', // DB: JSON string ↔ PHP array
'preferences' => 'collection', // DB: JSON string ↔ Collection
'created_at' => 'datetime', // DB: timestamp string → Carbon
'birthday' => 'date', // DB: '1990-01-15' → Carbon::date
'price' => 'decimal:2', // DB: float → formatted decimal
'api_token' => 'encrypted', // auto-encrypt in DB, decrypt on read
];
}
// Usage — automatic type conversion
$user = User::find(1);
$user->active; // true (not '1')
$user->age; // 25 (not '25')
$user->settings; // ['theme' => 'dark', 'lang' => 'en'] (not JSON string)
$user->created_at; // Carbon instance — $user->created_at->diffForHumans()
$user->settings = ['theme' => 'light']; // auto-serialized to JSON on save
$user->save();
// ENUM CAST (PHP 8.1+)
enum OrderStatus: string
{
case Pending = 'pending';
case Shipped = 'shipped';
case Delivered = 'delivered';
case Cancelled = 'cancelled';
}
class Order extends Model
{
protected $casts = [
'status' => OrderStatus::class, // DB: 'pending' → PHP: OrderStatus::Pending
];
}
$order = Order::find(1);
$order->status; // OrderStatus::Pending (enum instance)
$order->status === OrderStatus::Pending; // true
$order->status->value; // 'pending' (string value)
$order->status = OrderStatus::Shipped; // auto-saves 'shipped' in DB
// CUSTOM CAST — cast to a Value Object
class MoneyCast implements \Illuminate\Contracts\Database\Eloquent\CastsAttributes
{
public function get($model, string $key, mixed $value, array $attributes): Money
{
return new Money((int) $value, $attributes['currency'] ?? 'USD');
}
public function set($model, string $key, mixed $value, array $attributes): int
{
if (!$value instanceof Money) throw new \InvalidArgumentException('Expected Money object');
return $value->amount;
}
}
class Product extends Model
{
protected $casts = [
'price' => MoneyCast::class, // DB: integer (cents) ↔ PHP: Money object
];
}
$product = Product::find(1);
$product->price; // Money(1999, 'USD')
$product->price->amount; // 1999 (cents)
$product->price = new Money(2499, 'USD');
$product->save(); // stores 2499 in DB