> ## Documentation Index
> Fetch the complete documentation index at: https://kawax.biz/llms.txt
> Use this file to discover all available pages before exploring further.

# Accesores, mutadores y casts de Eloquent

> Aprende a transformar los valores de los atributos de tus modelos Eloquent al obtenerlos y al guardarlos. Se explican de forma práctica cómo definir accesores y mutadores, y cómo usar los casts integrados.

## 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.

```mermaid theme={null}
flowchart LR
  DB["Base de datos<br>(valor bruto)"] -->|"Al obtener<br>accesor/cast"| APP["Aplicación<br>(valor transformado)"]
  APP -->|"Al guardar<br>mutador/cast"| DB
```

## 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 theme={null}
<?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`.

```php theme={null}
$user = User::find(1);

$firstName = $user->first_name; // Valor con ucfirst() aplicado
```

<Info>
  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.
</Info>

### 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.

```php theme={null}
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()`.

```php theme={null}
protected function hash(): Attribute
{
    return Attribute::make(
        get: fn (string $value) => bcrypt(gzuncompress($value)),
    )->shouldCache();
}
```

Para desactivar la caché de objetos utiliza `withoutObjectCaching()`.

```php theme={null}
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.

```php theme={null}
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`.

```php theme={null}
$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.

```php theme={null}
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 theme={null}
<?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

| Cast                        | Descripción                          |
| --------------------------- | ------------------------------------ |
| `integer` / `int`           | Entero                               |
| `float` / `double` / `real` | Coma flotante                        |
| `decimal:<precisión>`       | Decimal con la precisión indicada    |
| `string`                    | Cadena                               |
| `boolean` / `bool`          | Booleano (incluye `0`/`1`)           |
| `array`                     | Convierte JSON en array y viceversa  |
| `object`                    | Convierte JSON en objeto y viceversa |
| `collection`                | Convierte JSON en colección          |
| `date`                      | Fecha (Carbon)                       |
| `datetime`                  | Fecha y hora (Carbon)                |
| `immutable_date`            | Fecha inmutable                      |
| `immutable_datetime`        | Fecha y hora inmutables              |
| `timestamp`                 | Timestamp UNIX                       |
| `hashed`                    | Se aplica un hash al guardar         |
| `encrypted`                 | Se cifra al guardar                  |

<Warning>
  Los atributos `null` no se convierten. Tampoco definas casts con el mismo nombre que una relación ni casts para la clave primaria.
</Warning>

### Cast Stringable

Con `AsStringable` puedes tratar el atributo como un objeto `Illuminate\Support\Stringable`.

```php theme={null}
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.

```php theme={null}
protected function casts(): array
{
    return [
        'options' => 'array',
    ];
}
```

```php theme={null}
$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.

```php theme={null}
$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.

```php theme={null}
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()`.

```php theme={null}
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.

```php theme={null}
protected function casts(): array
{
    return [
        'published_at' => 'datetime',
        'expires_at'   => 'immutable_datetime',
    ];
}
```

Si indicas un formato, se utilizará al serializar a JSON.

```php theme={null}
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).

```php theme={null}
use DateTimeInterface;

protected function serializeDate(DateTimeInterface $date): string
{
    return $date->format('Y-m-d');
}
```

<Tip>
  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.
</Tip>

## Cast a Enum

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

```php theme={null}
<?php

namespace App\Enums;

enum ServerStatus: string
{
    case Provisioned = 'provisioned';
    case Ready       = 'ready';
    case Archived    = 'archived';
}
```

```php theme={null}
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.

```php theme={null}
$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`.

```php theme={null}
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()`.

```php theme={null}
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`.

```shell theme={null}
php artisan make:cast AsJson
```

Para más detalles (patrón Value Object, casts inbound, Castables, etc.), consulta la página avanzada.

<Card title="Detalle de los casts personalizados" icon="book" href="/es/advanced/eloquent-casts">
  Descubre cómo implementar la interfaz CastsAttributes, así como patrones avanzados como Value Object o Castables.
</Card>

## Páginas relacionadas

<Card title="API resources de Eloquent" icon="link" href="/es/eloquent-resources">
  Aprende a usar las clases de recurso para convertir los modelos en respuestas JSON API coherentes.
</Card>


## Related topics

- [Casts personalizados de Eloquent](/es/advanced/eloquent-casts.md)
- [Actualización de Laravel 10 a 11](/es/blog/upgrade-10-to-11.md)
- [Nuevo ecosistema de análisis de código de Laravel — surveyor / ranger / roster](/es/blog/laravel-ecosystem-analysis.md)
- [Scopes de Eloquent](/es/advanced/eloquent-scopes.md)
- [Serialización en Eloquent](/es/eloquent-serialization.md)
