How to implement the CastsAttributes interface to build custom casts. Covers Value Object casts, inbound-only casts, cast parameters, and the Castable pattern.
Eloquent casts convert raw database values to PHP types when you read them, and convert them back when you write. You define them in the model’s casts method.
// src/Illuminate/Contracts/Database/Eloquent/CastsAttributes.phpinterface CastsAttributes{ /** * Convert the database value to a PHP value (reading). * * @param array<string, mixed> $attributes All attributes on the model */ public function get(Model $model, string $key, mixed $value, array $attributes); /** * Convert the PHP value to a database value (writing). * * @param array<string, mixed> $attributes All attributes on the model */ public function set(Model $model, string $key, mixed $value, array $attributes);}
The $attributes parameter gives you access to every column on the model, which makes multi-column Value Object casts possible.
This cast combines address_line_one and address_line_two into a single Address Value Object.
<?phpnamespace App\ValueObjects;use Illuminate\Contracts\Support\Arrayable;class Address implements Arrayable, \JsonSerializable{ public function __construct( public readonly string $lineOne, public readonly string $lineTwo, ) {} public function toArray(): array { return [ 'line_one' => $this->lineOne, 'line_two' => $this->lineTwo, ]; } public function jsonSerialize(): array { return $this->toArray(); }}
<?phpnamespace App\Casts;use App\ValueObjects\Address;use Illuminate\Contracts\Database\Eloquent\CastsAttributes;use Illuminate\Database\Eloquent\Model;use InvalidArgumentException;class AsAddress implements CastsAttributes{ public function get( Model $model, string $key, mixed $value, array $attributes, ): Address { return new Address( $attributes['address_line_one'], $attributes['address_line_two'], ); } /** * @return array<string, string> */ public function set( Model $model, string $key, mixed $value, array $attributes, ): array { if (! $value instanceof Address) { throw new InvalidArgumentException('The given value is not an Address instance.'); } return [ 'address_line_one' => $value->lineOne, 'address_line_two' => $value->lineTwo, ]; }}
When set returns an array, Eloquent uses the array keys as column names and writes each value to the corresponding column. For single-column casts, return a scalar value.
<?phpnamespace App\Models;use App\Casts\AsAddress;use Illuminate\Database\Eloquent\Model;class User extends Model{ protected function casts(): array { return [ 'address' => AsAddress::class, ]; }}
$user = User::find(1);// Access as a Value Objectecho $user->address->lineOne;// Assign a new Value Object; it is saved to the correct columns automatically$user->address = new Address('123 Main St', 'Apt 4B');$user->save();
Cast Value Objects are cached by Eloquent. Accessing the same attribute twice returns the same object instance.To disable this behavior, add the $withoutObjectCaching property to your cast class.
class AsAddress implements CastsAttributes{ public bool $withoutObjectCaching = true; // ...}
An inbound cast transforms a value only when it is written to the database. Reading returns the raw stored value. Use CastsInboundAttributes.Hashing is the canonical example — you hash a password when storing it, but you never reverse the transformation when reading.
php artisan make:cast AsHash --inbound
<?phpnamespace App\Casts;use Illuminate\Contracts\Database\Eloquent\CastsInboundAttributes;use Illuminate\Database\Eloquent\Model;class AsHash implements CastsInboundAttributes{ public function __construct( protected string|null $algorithm = null, ) {} public function set( Model $model, string $key, mixed $value, array $attributes, ): string { return is_null($this->algorithm) ? bcrypt($value) : hash($this->algorithm, $value); }}
A Value Object that implements Castable declares which cast class to use via a static castUsing method. The model does not need to know about the cast class at all.