Casting a column to a value object needed an accessor and a mutator on every model that had one, and the pair had to be kept in step by hand.
final class AsMoney implements CastsAttributes
{
public function get($model, string $key, $value, array $attributes): Money
{
return new Money((int) $value, $attributes['currency']);
}
public function set($model, string $key, $value, array $attributes): array
{
return ['total' => $value->cents(), 'currency' => $value->currency()];
}
}
// protected $casts = ['total' => AsMoney::class];
Returning an array from set is what lets one cast span two columns, which is the case the accessor pair handled worst. The $attributes parameter gives access to the other raw columns, so a cast can depend on a sibling — and that dependency is invisible from the $casts array, which is worth a comment. Inbound-only casts exist for a field that is written and never read back, which is the right tool for a hashed password.