Documentación de Laravel 10.x
Aquí encontrarás fragmentos de código de Laravel y consejos útiles sobre desarrollo web.
Los accesores, mutadores y la conversión de atributos te permiten transformar los valores de atributos Eloquent cuando los recuperas o los estableces en instancias del modelo. Por ejemplo, es posible que desees utilizar el encriptador de Laravel para cifrar un valor mientras se almacena en la base de datos y, luego, descifrar automáticamente el atributo cuando accedes a él en un modelo Eloquent. O, quizás quieras convertir una cadena JSON almacenada en tu base de datos a una matriz cuando se accede a ella a través de tu modelo Eloquent.
Un accesor transforma el valor de un atributo Eloquent cuando se accede a él. Para definir un accesor, crea un método protegido en tu modelo para representar el atributo accesible. El nombre de este método debe corresponder con la representación en "camel case" del verdadero atributo del modelo / columna de la base de datos cuando sea aplicable.
En este ejemplo, definiremos un accesor para el atributo first_name
. El accesor se llamará automáticamente cuando Eloquent intente recuperar el valor del atributo first_name
. Todos los métodos de accesor / mutador de atributos deben declarar un tipo de retorno Illuminate\Database\Eloquent\Casts\Attribute
:
<?php namespace App\Models; use Illuminate\Database\Eloquent\Casts\Attribute;use Illuminate\Database\Eloquent\Model; class User extends Model{ /** * Obtener el primer nombre del usuario. */ protected function firstName(): Attribute { return Attribute::make( get: fn (string $value) => ucfirst($value), ); }}
Todos los métodos de acceso devuelven una instancia de Attribute
, que define cómo se accederá al atributo y, opcionalmente, cómo se mutará. En este ejemplo, solo estamos definiendo cómo se accederá al atributo. Para hacerlo, proporcionamos el argumento get
al constructor de la clase Attribute
.
Como puedes ver, el valor original de la columna se pasa al accessor, lo que te permite manipular y devolver el valor. Para acceder al valor del accessor, simplemente puedes acceder al atributo first_name
en una instancia del modelo:
use App\Models\User; $user = User::find(1); $firstName = $user->first_name;
Nota Si deseas que estos valores calculados se agreguen a las representaciones de matriz / JSON de tu modelo, deberás anexarlos.
A veces, tu accessor puede necesitar transformar múltiples atributos del modelo en un solo "objeto de valor". Para hacerlo, tu cierre get
puede aceptar un segundo argumento $attributes
, que se suministrará automáticamente al cierre y contendrá una matriz de todos los atributos actuales del modelo:
use App\Support\Address;use Illuminate\Database\Eloquent\Casts\Attribute; /** * Interactuar con la dirección del usuario. */protected function address(): Attribute{ return Attribute::make( get: fn (mixed $value, array $attributes) => new Address( $attributes['address_line_one'], $attributes['address_line_two'], ), );}
Cuando devuelves objetos de valor desde accessors, cualquier cambio realizado en el objeto de valor se sincronizará automáticamente de nuevo al modelo antes de que se guarde el modelo. Esto es posible porque Eloquent retiene las instancias devueltas por accessors para poder devolver la misma instancia cada vez que se invoque el accessor:
use App\Models\User; $user = User::find(1); $user->address->lineOne = 'Updated Address Line 1 Value';$user->address->lineTwo = 'Updated Address Line 2 Value'; $user->save();
Sin embargo, a veces deseas habilitar el almacenamiento en caché para valores primitivos como cadenas y booleanos, especialmente si son computacionalmente intensivos. Para lograr esto, puedes invocar el método shouldCache
al definir tu accesor:
protected function hash(): Attribute{ return Attribute::make( get: fn (string $value) => bcrypt(gzuncompress($value)), )->shouldCache();}
Si deseas deshabilitar el comportamiento de almacenamiento en caché de objetos para los atributos, puedes invocar el método withoutObjectCaching
al definir el atributo:
/** * Interactuar con la dirección del usuario. */protected function address(): Attribute{ return Attribute::make( get: fn (mixed $value, array $attributes) => new Address( $attributes['address_line_one'], $attributes['address_line_two'], ), )->withoutObjectCaching();}
Un mutador transforma el valor de un atributo Eloquent cuando se establece. Para definir un mutador, puedes proporcionar el argumento set
al definir tu atributo. Definamos un mutador para el atributo first_name
. Este mutador se llamará automáticamente cuando intentemos establecer el valor del atributo first_name
en el modelo:
<?php namespace App\Models; use Illuminate\Database\Eloquent\Casts\Attribute;use Illuminate\Database\Eloquent\Model; class User extends Model{ /** * Interactuar con el primer nombre del usuario. */ protected function firstName(): Attribute { return Attribute::make( get: fn (string $value) => ucfirst($value), set: fn (string $value) => strtolower($value), ); }}
La clausura del mutador recibirá el valor que se está estableciendo en el atributo, lo que te permitirá manipular el valor y devolver el valor manipulado. Para usar nuestro mutador, solo necesitamos establecer el atributo first_name
en un modelo Eloquent:
use App\Models\User; $user = User::find(1); $user->first_name = 'Sally';
En este ejemplo, la cláusula set
se llamará con el valor Sally
. El mutador aplicará la función strtolower
al nombre y establecerá su valor resultante en la matriz interna $attributes
del modelo.
A veces, tu mutador puede necesitar establecer varios atributos en el modelo subyacente. Para hacerlo, puedes devolver un array desde la cláusula set
. Cada clave en el array debe corresponder con un atributo subyacente / columna de la base de datos asociado al modelo:
use App\Support\Address;use Illuminate\Database\Eloquent\Casts\Attribute; /** * Interactuar con la dirección del usuario. */protected function address(): Attribute{ return Attribute::make( get: fn (mixed $value, array $attributes) => new Address( $attributes['address_line_one'], $attributes['address_line_two'], ), set: fn (Address $value) => [ 'address_line_one' => $value->lineOne, 'address_line_two' => $value->lineTwo, ], );}
La conversión de atributos proporciona funcionalidades similares a los accesores y mutadores sin requerir que definas métodos adicionales en tu modelo. En su lugar, la propiedad $casts
de tu modelo proporciona un método conveniente para convertir atributos a tipos de datos comunes.
La propiedad $casts
debe ser un array donde la clave es el nombre del atributo que se está convirtiendo y el valor es el tipo al que deseas convertir la columna. Los tipos de conversión admitidos son:
array
AsStringable::class
boolean
collection
date
datetime
immutable_date
immutable_datetime
decimal:<precision>
double
encrypted
encrypted:array
encrypted:collection
encrypted:object
float
hashed
integer
object
real
string
timestamp
Para demostrar la conversión de atributos, casteemos el atributo is_admin
, que se almacena en nuestra base de datos como un entero (0
o 1
), a un valor booleano:
<?php namespace App\Models; use Illuminate\Database\Eloquent\Model; class User extends Model{ /** * Los atributos que deben ser convertidos. * * @var array */ protected $casts = [ 'is_admin' => 'boolean', ];}
Después de definir la conversión, el atributo is_admin
siempre se convertirá a un booleano cuando lo accedas, incluso si el valor subyacente se almacena en la base de datos como un entero:
$user = App\Models\User::find(1); if ($user->is_admin) { // ...}
Si necesitas agregar una nueva conversión temporal en tiempo de ejecución, puedes usar el método mergeCasts
. Estas definiciones de conversión se agregarán a cualquiera de las conversiones ya definidas en el modelo:
$user->mergeCasts([ 'is_admin' => 'integer', 'options' => 'object',]);
Advertencia Los atributos que son
null
no se convertirán. Además, nunca debes definir una conversión (o un atributo) que tenga el mismo nombre que una relación o asignar una conversión a la clave primaria del modelo.
Puedes usar la clase de conversión Illuminate\Database\Eloquent\Casts\AsStringable
para convertir un atributo del modelo a un objeto Illuminate\Support\Stringable
:
<?php namespace App\Models; use Illuminate\Database\Eloquent\Casts\AsStringable;use Illuminate\Database\Eloquent\Model; class User extends Model{ /** * Los atributos que deben ser convertidos. * * @var array */ protected $casts = [ 'directory' => AsStringable::class, ];}
La conversión a array
es particularmente útil al trabajar con columnas que se almacenan como JSON serializado. Por ejemplo, si tu base de datos tiene un tipo de campo JSON
o TEXT
que contiene JSON serializado, agregar la conversión a array
a ese atributo deserializará automáticamente el atributo a una matriz PHP cuando lo accedas en tu modelo Eloquent:
<?php namespace App\Models; use Illuminate\Database\Eloquent\Model; class User extends Model{ /** * Los atributos que deben ser convertidos. * * @var array */ protected $casts = [ 'options' => 'array', ];}
Una vez que se define la conversión, puedes acceder al atributo options
y se deserializará automáticamente de JSON a una matriz PHP. Cuando establezcas el valor del atributo options
, la matriz dada se serializará automáticamente de nuevo a JSON para su almacenamiento:
use App\Models\User; $user = User::find(1); $options = $user->options; $options['key'] = 'value'; $user->options = $options; $user->save();
Para actualizar un solo campo de un atributo JSON con una sintaxis más concisa, puedes usar el operador ->
al llamar al método update
:
$user = User::find(1); $user->update(['options->key' => 'value']);
Aunque la conversión estándar a array
es suficiente para muchas aplicaciones, tiene algunas desventajas. Dado que la conversión a array
devuelve un tipo primitivo, no es posible mutar directamente un índice del array. Por ejemplo, el siguiente código generará un error de PHP:
$user = User::find(1); $user->options['key'] = $value;
Para resolver esto, Laravel ofrece una conversión AsArrayObject
que convierte tu atributo JSON a una clase ArrayObject. Esta característica se implementa mediante la implementación de Laravel de conversiones personalizadas, lo que permite a Laravel almacenar en caché e inteligentemente transformar el objeto mutado para que los índices individuales puedan modificarse sin generar un error de PHP. Para usar la conversión AsArrayObject
, simplemente asígnala a un atributo:
use Illuminate\Database\Eloquent\Casts\AsArrayObject; /** * Los atributos que deben ser convertidos. * * @var array */protected $casts = [ 'options' => AsArrayObject::class,];
De manera similar, Laravel ofrece una conversión AsCollection
que convierte tu atributo JSON a una instancia de la clase Laravel Collection:
use Illuminate\Database\Eloquent\Casts\AsCollection; /** * Los atributos que deben ser convertidos. * * @var array */protected $casts = [ 'options' => AsCollection::class,];
Si deseas que la conversión AsCollection
instancie una clase de colección personalizada en lugar de la clase base de colección de Laravel, puedes proporcionar el nombre de la clase de colección como un argumento de conversión:
use App\Collections\OptionCollection;use Illuminate\Database\Eloquent\Casts\AsCollection; /** * Los atributos que deben ser convertidos. * * @var array */protected $casts = [ 'options' => AsCollection::class.':'.OptionCollection::class,];
Por defecto, Eloquent convertirá las columnas created_at
y updated_at
a instancias de Carbon, que extiende la clase DateTime
de PHP y proporciona una variedad de métodos útiles. Puedes convertir atributos de fecha adicionales definiendo conversiones de fecha adicionales dentro del array $casts
de tu modelo. Normalmente, las fechas deben convertirse usando los tipos de conversión datetime
o immutable_datetime
.
Al definir una conversión de date
o datetime
, también puedes especificar el formato de la fecha. Este formato se utilizará cuando el modelo se serialice a una matriz o JSON:
/** * Los atributos que deben ser convertidos. * * @var array */protected $casts = [ 'created_at' => 'datetime:Y-m-d',];
Cuando una columna se convierte en una fecha, puedes establecer el valor del atributo del modelo correspondiente a una marca de tiempo UNIX, una cadena de fecha (Y-m-d
), una cadena de fecha y hora o una instancia de DateTime
/ Carbon
. El valor de la fecha se convertirá y almacenará correctamente en tu base de datos.
Puedes personalizar el formato de serialización predeterminado para todas las fechas de tu modelo definiendo un método serializeDate
en tu modelo. Este método no afecta cómo se formatean tus fechas para su almacenamiento en la base de datos:
/** * Preparar una fecha para la serialización en array / JSON. */protected function serializeDate(DateTimeInterface $date): string{ return $date->format('Y-m-d');}
Para especificar el formato que se debe utilizar cuando se almacena realmente una fecha del modelo en tu base de datos, debes definir una propiedad $dateFormat
en tu modelo:
/** * El formato de almacenamiento de las columnas de fecha del modelo. * * @var string */protected $dateFormat = 'U';
De forma predeterminada, las conversiones date
y datetime
serializarán las fechas a una cadena de fecha ISO-8601 en UTC (AAAA-MM-DDTHH:MM:SS.uuuuuuZ
), independientemente de la zona horaria especificada en la opción de configuración timezone
de tu aplicación. Se recomienda encarecidamente utilizar este formato de serialización siempre y almacenar las fechas de tu aplicación en la zona horaria UTC, sin cambiar la opción de configuración timezone
de tu aplicación desde su valor predeterminado UTC
. Utilizar consistentemente la zona horaria UTC en toda tu aplicación proporcionará el máximo nivel de interoperabilidad con otras bibliotecas de manipulación de fechas escritas en PHP y JavaScript.
Si se aplica un formato personalizado a la conversión date
o datetime
, como datetime:Y-m-d H:i:s
, se utilizará la zona horaria interna de la instancia Carbon durante la serialización de la fecha. Típicamente, esto será la zona horaria especificada en la opción de configuración timezone
de tu aplicación.
Eloquent también te permite convertir los valores de atributos a Enums de PHP. Para lograr esto, puedes especificar el atributo y el enum que deseas convertir en el array $casts
de tu modelo:
use App\Enums\ServerStatus; /** * Los atributos que deben ser convertidos. * * @var array */protected $casts = [ 'status' => ServerStatus::class,];
Una vez que hayas definido la conversión en tu modelo, el atributo especificado se convertirá automáticamente a un enum y viceversa cuando interactúes con el atributo:
if ($server->status == ServerStatus::Provisioned) { $server->status = ServerStatus::Ready; $server->save();}
A veces, es posible que necesites que tu modelo almacene una matriz de valores de enum dentro de una sola columna. Para lograr esto, puedes utilizar las conversiones AsEnumArrayObject
o AsEnumCollection
proporcionadas por Laravel:
use App\Enums\ServerStatus;use Illuminate\Database\Eloquent\Casts\AsEnumCollection; /** * Los atributos que deben ser convertidos. * * @var array */protected $casts = [ 'statuses' => AsEnumCollection::class.':'.ServerStatus::class,];
La conversión encrypted
cifrará el valor del atributo de un modelo utilizando las funciones de cifrado integradas de Laravel. Además, las conversiones encrypted:array
, encrypted:collection
, encrypted:object
, AsEncryptedArrayObject
y AsEncryptedCollection
funcionan como sus contrapartes no cifradas; sin embargo, como podrías esperar, el valor subyacente se cifra cuando se almacena en tu base de datos.
Dado que la longitud final del texto cifrado no es predecible y es más larga que su contraparte en texto plano, asegúrate de que la columna de base de datos asociada sea de tipo TEXT
o más grande. Además, dado que los valores se cifran en la base de datos, no podrás realizar consultas ni buscar valores de atributos cifrados.
Como sabrás, Laravel cifra las cadenas utilizando el valor key
especificado en el archivo de configuración app
de tu aplicación. Típicamente, este valor corresponde al valor de la variable de entorno APP_KEY
. Si necesitas rotar la clave de cifrado de tu aplicación, deberás cifrar manualmente de nuevo tus atributos cifrados utilizando la nueva clave.
A veces, es posible que necesites aplicar conversiones mientras ejecutas una consulta, como al seleccionar un valor sin procesar de una tabla. Por ejemplo, considera la siguiente consulta:
use App\Models\Post;use App\Models\User; $users = User::select([ 'users.*', 'last_posted_at' => Post::selectRaw('MAX(created_at)') ->whereColumn('user_id', 'users.id')])->get();
El atributo last_posted_at
en los resultados de esta consulta será una cadena simple. Sería maravilloso si pudiéramos aplicar una conversión datetime
a este atributo al ejecutar la consulta. Afortunadamente, podemos lograr esto utilizando el método withCasts
:
$users = User::select([ 'users.*', 'last_posted_at' => Post::selectRaw('MAX(created_at)') ->whereColumn('user_id', 'users.id')])->withCasts([ 'last_posted_at' => 'datetime'])->get();
Laravel tiene una variedad de tipos de conversión integrados y útiles; sin embargo, ocasionalmente es posible que necesites definir tus propios tipos de conversión. Para crear una conversión, ejecuta el comando Artisan make:cast
. La nueva clase de conversión se ubicará en tu directorio app/Casts
:
php artisan make:cast Json
Todas las clases de conversión personalizadas implementan la interfaz CastsAttributes
. Las clases que implementan esta interfaz deben definir un método get
y set
. El método get
es responsable de transformar un valor sin procesar de la base de datos en un valor convertido, mientras que el método set
debe transformar un valor convertido en un valor sin procesar que se puede almacenar en la base de datos. Como ejemplo, reimplementaremos el tipo de conversión integrado json
como un tipo de conversión personalizado:
<?php namespace App\Casts; use Illuminate\Contracts\Database\Eloquent\CastsAttributes;use Illuminate\Database\Eloquent\Model; class Json implements CastsAttributes{ /** * Convertir el valor dado. * * @param array<string, mixed> $attributes * @return array<string, mixed> */ public function get(Model $model, string $key, mixed $value, array $attributes): array { return json_decode($value, true); } /** * Preparar el valor dado para su almacenamiento. * * @param array<string, mixed> $attributes */ public function set(Model $model, string $key, mixed $value, array $attributes): string { return json_encode($value); }}
Una vez que hayas definido un tipo de conversión personalizado, puedes adjuntarlo a un atributo del modelo utilizando el nombre de la clase:
<?php namespace App\Models; use App\Casts\Json;use Illuminate\Database\Eloquent\Model; class User extends Model{ /** * Los atributos que deben ser convertidos. * * @var array */ protected $casts = [ 'options' => Json::class, ];}
No estás limitado a convertir valores a tipos primitivos. También puedes convertir valores a objetos. La definición de conversiones personalizadas que convierten valores a objetos es muy similar a la conversión a tipos primitivos; sin embargo, el método set
debería devolver un array de pares clave/valor que se utilizarán para establecer valores sin procesar, almacenables en el modelo.
Como ejemplo, definiremos una clase de conversión personalizada que convierte varios valores del modelo en un único valor de objeto Address
. Supondremos que el valor Address
tiene dos propiedades públicas: lineOne
y lineTwo
:
<?php namespace App\Casts; use App\ValueObjects\Address as AddressValueObject;use Illuminate\Contracts\Database\Eloquent\CastsAttributes;use Illuminate\Database\Eloquent\Model;use InvalidArgumentException; class Address implements CastsAttributes{ /** * Convertir el valor dado. * * @param array<string, mixed> $attributes */ public function get(Model $model, string $key, mixed $value, array $attributes): AddressValueObject { return new AddressValueObject( $attributes['address_line_one'], $attributes['address_line_two'] ); } /** * Preparar el valor dado para su almacenamiento. * * @param array<string, mixed> $attributes * @return array<string, string> */ public function set(Model $model, string $key, mixed $value, array $attributes): array { if (! $value instanceof AddressValueObject) { throw new InvalidArgumentException('The given value is not an Address instance.'); } return [ 'address_line_one' => $value->lineOne, 'address_line_two' => $value->lineTwo, ]; }}
Cuando se convierten a objetos de valor, cualquier cambio realizado en el objeto de valor se sincronizará automáticamente de nuevo al modelo antes de que se guarde el modelo:
use App\Models\User; $user = User::find(1); $user->address->lineOne = 'Updated Address Value'; $user->save();
Nota Si planeas serializar modelos Eloquent que contienen objetos de valor a JSON o matrices, debes implementar las interfaces
Illuminate\Contracts\Support\Arrayable
yJsonSerializable
en el objeto de valor.
Cuando los atributos que se convierten a objetos de valor se resuelven, se almacenan en caché mediante Eloquent. Por lo tanto, la misma instancia de objeto se devolverá si se accede al atributo de nuevo.
Si deseas desactivar el comportamiento de almacenamiento en caché de objetos de clases de conversión personalizadas, puedes declarar una propiedad pública withoutObjectCaching
en tu clase de conversión personalizada:
class Address implements CastsAttributes{ public bool $withoutObjectCaching = true; // ...}
Cuando un modelo Eloquent se convierte a un array o JSON utilizando los métodos toArray
y toJson
, tus objetos de valor de conversión personalizada generalmente se serializarán siempre y cuando implementen las interfaces Illuminate\Contracts\Support\Arrayable
e JsonSerializable
. Sin embargo, al usar objetos de valor proporcionados por bibliotecas de terceros, es posible que no tengas la capacidad de agregar estas interfaces al objeto.
Por lo tanto, puedes especificar que tu clase de conversión personalizada será la responsable de serializar el objeto de valor. Para hacerlo, tu clase de conversión personalizada debe implementar la interfaz Illuminate\Contracts\Database\Eloquent\SerializesCastableAttributes
. Esta interfaz establece que tu clase debe contener un método serialize
que debe devolver la forma serializada de tu objeto de valor:
/** * Obtener la representación serializada del valor. * * @param array<string, mixed> $attributes */public function serialize(Model $model, string $key, mixed $value, array $attributes): string{ return (string) $value;}
Ocasionalmente, es posible que necesites escribir una clase de conversión personalizada que solo transforme valores que se están estableciendo en el modelo y no realice ninguna operación cuando se recuperen atributos del modelo.
Las conversiones personalizadas solo de entrada deben implementar la interfaz CastsInboundAttributes
, que solo requiere que se defina un método set
. El comando Artisan make:cast
puede invocarse con la opción --inbound
para generar una clase de conversión solo de entrada:
php artisan make:cast Hash --inbound
Un ejemplo clásico de una conversión solo de entrada es una conversión de "hashing". Por ejemplo, podríamos definir una conversión que cifra los valores de entrada mediante un algoritmo dado:
<?php namespace App\Casts; use Illuminate\Contracts\Database\Eloquent\CastsInboundAttributes;use Illuminate\Database\Eloquent\Model; class Hash implements CastsInboundAttributes{ /** * Crear una nueva instancia de la clase de conversión. */ public function __construct( protected string|null $algorithm = null, ) {} /** * Preparar el valor dado para su almacenamiento. * * @param array<string, mixed> $attributes */ public function set(Model $model, string $key, mixed $value, array $attributes): string { return is_null($this->algorithm) ? bcrypt($value) : hash($this->algorithm, $value); }}
Cuando se adjunta una conversión personalizada a un modelo, se pueden especificar parámetros de conversión separándolos del nombre de la clase mediante el carácter :
y delimitando múltiples parámetros con comas. Los parámetros se pasarán al constructor de la clase de conversión:
/** * Los atributos que deben ser convertidos. * * @var array */protected $casts = [ 'secret' => Hash::class.':sha256',];
Es posible que desees permitir que los objetos de valor de tu aplicación definan sus propias clases de conversión personalizadas. En lugar de adjuntar la clase de conversión personalizada a tu modelo, también puedes adjuntar una clase de objeto de valor que implemente la interfaz Illuminate\Contracts\Database\Eloquent\Castable
:
use App\Models\Address; protected $casts = [ 'address' => Address::class,];
Los objetos que implementan la interfaz Castable
deben definir un método castUsing
que devuelva el nombre de la clase del casteo personalizado que se encarga de realizar el casteo hacia y desde la clase Castable
:
<?php namespace App\Models; use Illuminate\Contracts\Database\Eloquent\Castable;use App\Casts\Address as AddressCast; class Address implements Castable{ /** * Obtener el nombre de la clase de conversión a utilizar al convertir desde / hacia este objetivo de conversión. * * @param array<string, mixed> $arguments */ public static function castUsing(array $arguments): string { return AddressCast::class; }}
Cuando uses clases Castable
, aún puedes proporcionar argumentos en la definición de $casts
. Los argumentos se pasarán al método castUsing
:
use App\Models\Address; protected $casts = [ 'address' => Address::class.':argument',];
Al combinar "castables" con las clases anónimas de PHP, puedes definir un objeto de valor y su lógica de casting como un único objeto castable. Para lograr esto, devuelve una clase anónima desde el método castUsing
de tu objeto de valor. La clase anónima debe implementar la interfaz CastsAttributes
:
<?php namespace App\Models; use Illuminate\Contracts\Database\Eloquent\Castable;use Illuminate\Contracts\Database\Eloquent\CastsAttributes;use Illuminate\Database\Eloquent\Model; class Address implements Castable{ // ... /** * Obtener la clase de conversión a utilizar al convertir desde / hacia este objetivo de conversión. * * @param array<string, mixed> $arguments */ public static function castUsing(array $arguments): CastsAttributes { return new class 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'] ); } public function set(Model $model, string $key, mixed $value, array $attributes): array { return [ 'address_line_one' => $value->lineOne, 'address_line_two' => $value->lineTwo, ]; } }; }}