Saltar al contenido principal

Introducción

Los accesores, mutadores y casts de atributos son mecanismos que transforman los valores de los atributos de un modelo Eloquent al obtenerlos o al asignarlos desde la instancia.
  • Accesores: transforman el valor bruto que llega de la BD antes de exponerlo a la aplicación.
  • Mutadores: transforman el valor asignado por la aplicación antes de guardarlo en la BD.
  • Casts: permiten declarar la conversión de tipo del atributo sin escribir métodos adicionales.

Definir un accesor

Para definir un accesor añade al modelo un método protected. El nombre del método se escribe en camelCase y su tipo de retorno debe ser Illuminate\Database\Eloquent\Casts\Attribute.
<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Casts\Attribute;
use Illuminate\Database\Eloquent\Model;

class User extends Model
{
    /**
     * Obtiene el nombre del usuario.
     */
    protected function firstName(): Attribute
    {
        return Attribute::make(
            get: fn (string $value) => ucfirst($value),
        );
    }
}
La closure get recibe el valor bruto de la BD. Desde la instancia del modelo puedes acceder al atributo como propiedad first_name.
$user = User::find(1);

$firstName = $user->first_name; // Valor con ucfirst() aplicado
Si quieres que el valor calculado por el accesor se incluya en el JSON o el array del modelo, añade el atributo en snake_case a la propiedad $appends del modelo.

Crear un value object a partir de varios atributos

La closure get recibe como segundo argumento $attributes (todos los atributos del modelo). Así puedes combinar varias columnas y devolver un value object.
use App\Support\Address;
use Illuminate\Database\Eloquent\Casts\Attribute;

protected function address(): Attribute
{
    return Attribute::make(
        get: fn (mixed $value, array $attributes) => new Address(
            $attributes['address_line_one'],
            $attributes['address_line_two'],
        ),
    );
}

Caché del accesor

Los accesores que devuelven un value object devuelven automáticamente la misma instancia gracias a la caché de Eloquent. Para cachear también valores primitivos (cadenas, números) llama a shouldCache().
protected function hash(): Attribute
{
    return Attribute::make(
        get: fn (string $value) => bcrypt(gzuncompress($value)),
    )->shouldCache();
}
Para desactivar la caché de objetos utiliza withoutObjectCaching().
protected function address(): Attribute
{
    return Attribute::make(
        get: fn (mixed $value, array $attributes) => new Address(
            $attributes['address_line_one'],
            $attributes['address_line_two'],
        ),
    )->withoutObjectCaching();
}

Definir un mutador

El mutador se define en el argumento set de Attribute::make(). Puedes combinarlo con el accesor en el mismo método.
protected function firstName(): Attribute
{
    return Attribute::make(
        get: fn (string $value) => ucfirst($value),
        set: fn (string $value) => strtolower($value),
    );
}
Cuando asignes un valor al modelo se ejecutará la closure set.
$user = User::find(1);
$user->first_name = 'SALLY'; // Se guarda 'sally' tras aplicar strtolower()

Escribir en varios atributos

Si la closure set devuelve un array puedes actualizar varias columnas de golpe.
use App\Support\Address;
use Illuminate\Database\Eloquent\Casts\Attribute;

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,
        ],
    );
}

Casts de atributos

Un cast es una forma abreviada de declarar la conversión de tipo de un atributo sin necesidad de escribir accesores ni mutadores. Devuélvelos en un array desde el método casts() del modelo.
<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Model;

class User extends Model
{
    protected function casts(): array
    {
        return [
            'is_admin'   => 'boolean',
            'score'      => 'float',
            'settings'   => 'array',
            'created_at' => 'datetime',
        ];
    }
}

Casts integrados

CastDescripción
integer / intEntero
float / double / realComa flotante
decimal:<precisión>Decimal con la precisión indicada
stringCadena
boolean / boolBooleano (incluye 0/1)
arrayConvierte JSON en array y viceversa
objectConvierte JSON en objeto y viceversa
collectionConvierte JSON en colección
dateFecha (Carbon)
datetimeFecha y hora (Carbon)
immutable_dateFecha inmutable
immutable_datetimeFecha y hora inmutables
timestampTimestamp UNIX
hashedSe aplica un hash al guardar
encryptedSe cifra al guardar
Los atributos null no se convierten. Tampoco definas casts con el mismo nombre que una relación ni casts para la clave primaria.

Cast Stringable

Con AsStringable puedes tratar el atributo como un objeto Illuminate\Support\Stringable.
use Illuminate\Database\Eloquent\Casts\AsStringable;

protected function casts(): array
{
    return [
        'bio' => AsStringable::class,
    ];
}

Casts de arrays y JSON

Puedes tratar de forma transparente columnas JSON/TEXT como arrays PHP.
protected function casts(): array
{
    return [
        'options' => 'array',
    ];
}
$user = User::find(1);

// Se obtiene automáticamente como array PHP
$options = $user->options;

// Al modificarlo y guardar se serializa a JSON automáticamente
$user->options = array_merge($options, ['theme' => 'dark']);
$user->save();
Con el operador -> puedes actualizar únicamente una clave concreta del JSON.
$user->update(['options->theme' => 'dark']);

Casts AsArrayObject y AsCollection

El cast array estándar da error si intentas modificar directamente un offset del array. Con AsArrayObject o AsCollection esquivas ese problema.
use Illuminate\Database\Eloquent\Casts\AsArrayObject;
use Illuminate\Database\Eloquent\Casts\AsCollection;

protected function casts(): array
{
    return [
        'options' => AsArrayObject::class,   // Se trata como ArrayObject
        'tags'    => AsCollection::class,    // Se trata como Collection
    ];
}
Si quieres usar una colección propia, indícalo con using().
use App\Collections\TagCollection;
use Illuminate\Database\Eloquent\Casts\AsCollection;

protected function casts(): array
{
    return [
        'tags' => AsCollection::using(TagCollection::class),
    ];
}

Casts de fecha y hora

created_at y updated_at se convierten a Carbon por defecto. Puedes declarar otras columnas de fecha del mismo modo.
protected function casts(): array
{
    return [
        'published_at' => 'datetime',
        'expires_at'   => 'immutable_datetime',
    ];
}
Si indicas un formato, se utilizará al serializar a JSON.
protected function casts(): array
{
    return [
        'published_at' => 'datetime:Y-m-d',
    ];
}
Si quieres cambiar el formato de serialización por defecto de todas las fechas, sobrescribe serializeDate() (no afecta al formato con el que se guardan en BD).
use DateTimeInterface;

protected function serializeDate(DateTimeInterface $date): string
{
    return $date->format('Y-m-d');
}
Con immutable_datetime obtienes CarbonImmutable en vez de Carbon. Como no puedes modificar la instancia original, resulta más fácil escribir código sin efectos secundarios al manipular fechas.

Cast a Enum

Puedes usar enums respaldados (backed enums) de PHP 8.1+ como cast.
<?php

namespace App\Enums;

enum ServerStatus: string
{
    case Provisioned = 'provisioned';
    case Ready       = 'ready';
    case Archived    = 'archived';
}
use App\Enums\ServerStatus;

protected function casts(): array
{
    return [
        'status' => ServerStatus::class,
    ];
}
En la BD se guarda el valor respaldado (string o int) y al recuperarlo se convierte en la instancia del enum.
$server = Server::find(1);

if ($server->status === ServerStatus::Provisioned) {
    $server->status = ServerStatus::Ready;
    $server->save();
}

Cast de un array de enums

Si quieres almacenar varios valores de un enum como array en una única columna, utiliza AsEnumCollection.
use App\Enums\ServerStatus;
use Illuminate\Database\Eloquent\Casts\AsEnumCollection;

protected function casts(): array
{
    return [
        'statuses' => AsEnumCollection::of(ServerStatus::class),
    ];
}

Casts en tiempo de consulta

Para aplicar casts de forma dinámica al ejecutar una consulta utiliza withCasts().
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'),
])->withCasts([
    'last_posted_at' => 'datetime',
])->get();

Casts personalizados

También puedes crear tus propias clases de cast. Implementa la interfaz CastsAttributes y define los métodos get y set.
php artisan make:cast AsJson
Para más detalles (patrón Value Object, casts inbound, Castables, etc.), consulta la página avanzada.

Detalle de los casts personalizados

Descubre cómo implementar la interfaz CastsAttributes, así como patrones avanzados como Value Object o Castables.

Páginas relacionadas

API resources de Eloquent

Aprende a usar las clases de recurso para convertir los modelos en respuestas JSON API coherentes.
Última modificación el 13 de julio de 2026