Saltar al contenido principal

Qué son los casts

Los casts de Eloquent convierten los valores en bruto obtenidos de la base de datos a tipos de PHP y, al guardar, aplican la conversión inversa. Se declaran en el método casts.
protected function casts(): array
{
    return [
        'is_admin'   => 'boolean',
        'settings'   => 'array',
        'created_at' => 'datetime',
    ];
}

Tipos de casts integrados

Lista de los casts que Laravel ofrece de forma predeterminada.
CastDescripción
integer / intConvierte a entero.
float / doubleConvierte a punto flotante.
stringConvierte a cadena.
boolean / boolConvierte a booleano (incluidos 0/1).
arrayCadena JSON ↔ array de PHP.
collectionCadena JSON ↔ instancia de Collection.
objectCadena JSON ↔ instancia stdClass.
datetimeCadena ↔ instancia de Carbon.
immutable_datetimeCadena ↔ instancia de CarbonImmutable.
dateCadena ↔ Carbon (sin hora).
timestampCadena ↔ timestamp UNIX.
encryptedCifra al guardar, descifra al leer.
hashedAplica hash al guardar (se combina con casts de solo lectura).
AsStringable::classCadena ↔ objeto Stringable.
AsArrayObject::classJSON ↔ instancia de ArrayObject.
AsCollection::classJSON ↔ instancia de Collection.
AsArrayObject y AsCollection se implementan internamente en Laravel como casts personalizados para permitir modificar directamente offsets concretos del array.

Crear una clase de cast personalizado

Cuando los casts integrados no cubren la conversión que necesitas, crea un cast personalizado implementando la interfaz CastsAttributes.

Definición de la interfaz

El contrato del framework se define así:
// src/Illuminate/Contracts/Database/Eloquent/CastsAttributes.php

interface CastsAttributes
{
    /**
     * Convierte el valor en bruto de la BD a un valor PHP (lectura)
     *
     * @param  array<string, mixed>  $attributes  todos los atributos del modelo
     */
    public function get(Model $model, string $key, mixed $value, array $attributes);

    /**
     * Convierte un valor PHP a una forma persistible en la BD (escritura)
     *
     * @param  array<string, mixed>  $attributes  todos los atributos del modelo
     */
    public function set(Model $model, string $key, mixed $value, array $attributes);
}
Como el argumento $attributes contiene todos los atributos del modelo, es posible hacer conversiones que abarquen varias columnas (ver el patrón Value Object más abajo).

Implementación básica de un cast personalizado

Genera el esqueleto con el comando make:cast.
php artisan make:cast AsMoney
Se crea app/Casts/AsMoney.php. Como ejemplo, vamos a implementar un cast que convierte un importe (almacenado como entero) en un Value Object Money.
<?php

namespace App\Casts;

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

class AsMoney implements CastsAttributes
{
    /**
     * Convierte el entero de la BD (en yenes) a un objeto Money
     */
    public function get(
        Model $model,
        string $key,
        mixed $value,
        array $attributes,
    ): Money {
        return new Money((int) $value);
    }

    /**
     * Convierte el objeto Money en el entero que se guarda en la BD
     */
    public function set(
        Model $model,
        string $key,
        mixed $value,
        array $attributes,
    ): int {
        if ($value instanceof Money) {
            return $value->amount;
        }

        return (int) $value;
    }
}
Aplica el cast en el modelo.
<?php

namespace App\Models;

use App\Casts\AsMoney;
use Illuminate\Database\Eloquent\Model;

class Order extends Model
{
    protected function casts(): array
    {
        return [
            'price' => AsMoney::class,
        ];
    }
}
Con esto, $order->price devuelve una instancia de Money.

Casts de Value Object

Patrón para tratar varias columnas de la BD como un único Value Object.

Ejemplo: cast de dirección

Combina las dos columnas address_line_one y address_line_two en un Value Object Address.
<?php

namespace 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();
    }
}
<?php

namespace App\Casts;

use App\ValueObjects\Address;
use Illuminate\Contracts\Database\Eloquent\CastsAttributes;
use Illuminate\Database\Eloquent\Model;
use InvalidArgumentException;

class AsAddress implements CastsAttributes
{
    /**
     * Construye el objeto Address a partir de varias columnas
     */
    public function get(
        Model $model,
        string $key,
        mixed $value,
        array $attributes,
    ): Address {
        return new Address(
            $attributes['address_line_one'],
            $attributes['address_line_two'],
        );
    }

    /**
     * Descompone el objeto Address en un array por columnas
     *
     * @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,
        ];
    }
}
Si set devuelve un array, Eloquent interpreta las claves como nombres de columna y guarda cada valor en su columna correspondiente. En casts de una sola columna se devuelve una cadena o un entero.
Aplicación al modelo y uso:
<?php

namespace 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);

// Puedes obtenerlo como objeto Address
echo $user->address->lineOne;

// Al modificar el Value Object, los cambios se reflejan automáticamente en la BD al guardar
$user->address = new Address('123 Main St', 'Apt 4B');
$user->save();

Caché del Value Object

Eloquent cachea los atributos convertidos en Value Objects. Si accedes dos veces al mismo atributo, se devuelve la misma instancia. Si quieres desactivar la caché, añade la propiedad $withoutObjectCaching a la clase del cast.
class AsAddress implements CastsAttributes
{
    public bool $withoutObjectCaching = true;

    // ...
}

Casts entrantes (solo escritura)

Casts que solo aplican la conversión al escribir en la BD, sin transformar nada al leer. Se implementan mediante la interfaz CastsInboundAttributes. Un uso típico es el hashing. Solo se transforma al guardar contraseñas o valores secretos; al leer se devuelve el hash tal cual.
php artisan make:cast AsHash --inbound
<?php

namespace 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,
    ) {}

    /**
     * Aplica hash al guardar
     */
    public function set(
        Model $model,
        string $key,
        mixed $value,
        array $attributes,
    ): string {
        return is_null($this->algorithm)
            ? bcrypt($value)
            : hash($this->algorithm, $value);
    }
}

Parámetros de cast

Para pasar parámetros a un cast personalizado, especifícalos tras el nombre de la clase separados por dos puntos. Varios parámetros se separan por comas.
protected function casts(): array
{
    return [
        'secret' => AsHash::class.':sha256',
        'data'   => AsHash::class.':sha512',
    ];
}
Los parámetros se pasan al constructor de la clase del cast.
class AsHash implements CastsInboundAttributes
{
    public function __construct(
        protected string|null $algorithm = null, // recibe ':sha256'
    ) {}
}

Castables: colocar la lógica de cast en el propio Value Object

Un Value Object que implementa la interfaz Castable proporciona un método castUsing que devuelve su clase de cast. Con esto, el modelo no necesita conocer la clase de cast y la lógica de dominio queda mejor organizada.
<?php

namespace App\ValueObjects;

use App\Casts\AsAddress;
use Illuminate\Contracts\Database\Eloquent\Castable;

class Address implements Castable
{
    /**
     * Devuelve la clase que se usará para castear este objeto
     *
     * @param  array<string, mixed>  $arguments
     */
    public static function castUsing(array $arguments): string
    {
        return AsAddress::class;
    }
}
En el modelo, indica la clase del Value Object en lugar de la del cast.
protected function casts(): array
{
    return [
        'address' => Address::class,
    ];
}
Combinando Castable con una clase anónima puedes reunir el Value Object y la lógica de cast en un solo archivo.
class Address implements Castable
{
    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,
                ];
            }
        };
    }
}

Interacción con $appends y $hidden

Los casts y $appends / $hidden son mecanismos independientes, pero al combinarlos hay que tener cuidado.
class User extends Model
{
    protected function casts(): array
    {
        return [
            'address' => AsAddress::class,
        ];
    }

    // Excluye address en toArray() / toJson()
    protected $hidden = ['address_line_one', 'address_line_two'];

    // Incluye el address casteado en la serialización
    protected $appends = ['address'];
}
En $hidden se especifican nombres de columna de la BD. No debes indicar el atributo generado por el cast (address), sino las columnas originales (address_line_one, address_line_two).

Añadir casts en tiempo de ejecución

Cuando quieras añadir un cast solo para una consulta o una petición concreta, usa el método mergeCasts.
$user = User::find(1);

$user->mergeCasts([
    'extra_data' => 'array',
]);

Próximos pasos

Observers de Eloquent y eventos del modelo

Aprende a engancharte a los eventos del ciclo de vida (guardar, borrar, etc.) del modelo para añadir procesamiento.
Última modificación el 13 de julio de 2026