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

# API resources de Eloquent

> Aprende a transformar tus modelos Eloquent en respuestas JSON coherentes para una API. Se explican de forma práctica desde la creación de la clase resource hasta los campos condicionales y la integración con la paginación.

## Qué son los API resources

Cuando construyes una API y devuelves modelos Eloquent tal cual como JSON, puedes filtrar mal columnas que querías ocultar o enviar al cliente una gran cantidad de datos que no necesita.

Los **API resources de Eloquent** son una capa de transformación entre los modelos y la respuesta JSON. Con el método `toArray()` puedes definir explícitamente qué se incluye en la respuesta y con qué formato.

```mermaid theme={null}
flowchart LR
  A["Modelo Eloquent<br>(User)"] --> B["Clase resource<br>(UserResource)"]
  B --> C["Respuesta JSON<br>{ id, name, email }"]
```

Sus principales ventajas son:

* Control total sobre los campos que se incluyen en la respuesta.
* Un único lugar en el que renombrar campos y transformar valores.
* Posibilidad de incluir campos condicionalmente.
* Anidamiento de relaciones para mantener una estructura consistente.

## Crear un resource

Genera una clase resource con el comando Artisan `make:resource`.

```shell theme={null}
php artisan make:resource UserResource
```

La clase generada se ubica en el directorio `app/Http/Resources`.

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

namespace App\Http\Resources;

use Illuminate\Http\Request;
use Illuminate\Http\Resources\Json\JsonResource;

class UserResource extends JsonResource
{
    public function toArray(Request $request): array
    {
        return [
            'id' => $this->id,
            'name' => $this->name,
            'email' => $this->email,
            'created_at' => $this->created_at,
            'updated_at' => $this->updated_at,
        ];
    }
}
```

Puedes acceder directamente a las propiedades del modelo con `$this`, porque la clase resource actúa como proxy hacia el modelo subyacente.

## Uso en un controlador

Puedes devolver el resource definido desde un controlador o una ruta.

```php theme={null}
use App\Http\Resources\UserResource;
use App\Models\User;

Route::get('/users/{user}', function (User $user) {
    return new UserResource($user);
});
```

También puedes usar el método `toResource()` del modelo.

```php theme={null}
Route::get('/users/{user}', function (User $user) {
    return $user->toResource();
});
```

`toResource()` localiza automáticamente la clase resource correspondiente al modelo (`UserResource`).

Por defecto, la respuesta se envuelve en la clave `data`.

```json theme={null}
{
  "data": {
    "id": 1,
    "name": "Ana García",
    "email": "ana@example.com",
    "created_at": "2024-01-15T10:00:00.000000Z",
    "updated_at": "2024-01-15T10:00:00.000000Z"
  }
}
```

## Colecciones de resources

Para devolver varios modelos utiliza el método `collection()`.

```php theme={null}
use App\Http\Resources\UserResource;
use App\Models\User;

Route::get('/users', function () {
    return UserResource::collection(User::all());
});
```

O bien `toResourceCollection()` sobre una colección Eloquent.

```php theme={null}
return User::all()->toResourceCollection();
```

### Resource collection personalizado

Si quieres añadir metadatos al conjunto de la colección, crea un resource collection propio.

```shell theme={null}
php artisan make:resource UserCollection
```

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

namespace App\Http\Resources;

use Illuminate\Http\Request;
use Illuminate\Http\Resources\Json\ResourceCollection;

class UserCollection extends ResourceCollection
{
    public function toArray(Request $request): array
    {
        return [
            'data' => $this->collection,
            'links' => [
                'self' => route('users.index'),
            ],
        ];
    }
}
```

```php theme={null}
Route::get('/users', function () {
    return new UserCollection(User::all());
});
```

## Transformar y renombrar campos

Puedes renombrar campos y transformar valores dentro de `toArray()`.

```php theme={null}
public function toArray(Request $request): array
{
    return [
        'id' => $this->id,
        'full_name' => $this->name,                         // Renombrar campo
        'email_address' => $this->email,                    // Renombrar campo
        'role' => strtoupper($this->role),                  // Transformar valor
        'registered_at' => $this->created_at->toDateString(), // Formatear fecha
    ];
}
```

## Campos condicionales

### `when()`: añadir un campo si se cumple una condición

Cuando solo quieras incluir un campo si se cumple una condición, utiliza `when()`.

```php theme={null}
public function toArray(Request $request): array
{
    return [
        'id' => $this->id,
        'name' => $this->name,
        'email' => $this->email,
        // Se incluye solo si el usuario autenticado es administrador
        'secret_token' => $this->when(
            $request->user()?->isAdmin(),
            $this->secret_token
        ),
    ];
}
```

Si la condición de `when()` es `false`, la clave se elimina por completo de la respuesta.

### `mergeWhen()`: añadir varios campos con la misma condición

Para incluir varios campos bajo la misma condición, utiliza `mergeWhen()`.

```php theme={null}
public function toArray(Request $request): array
{
    return [
        'id' => $this->id,
        'name' => $this->name,
        $this->mergeWhen($request->user()?->isAdmin(), [
            'admin_note' => $this->admin_note,
            'internal_id' => $this->internal_id,
        ]),
    ];
}
```

### `whenLoaded()`: incluir la relación solo si está cargada

Al incluir una relación solo cuando se haya cargado con eager loading, evitas el problema N+1 sin sacrificar la flexibilidad de la respuesta.

```php theme={null}
use App\Http\Resources\PostResource;

public function toArray(Request $request): array
{
    return [
        'id' => $this->id,
        'name' => $this->name,
        'email' => $this->email,
        // Solo se incluye si posts se ha cargado con with()
        'posts' => PostResource::collection($this->whenLoaded('posts')),
    ];
}
```

Puedes decidir en el controlador si cargar o no la relación.

```php theme={null}
// Con los posts incluidos
return new UserResource($user->load('posts'));

// Sin los posts
return new UserResource($user);
```

### `whenCounted()`: incluir un conteo si se ha cargado

Incluye el conteo obtenido con `loadCount()` solo si está disponible.

```php theme={null}
public function toArray(Request $request): array
{
    return [
        'id' => $this->id,
        'name' => $this->name,
        'posts_count' => $this->whenCounted('posts'),
    ];
}
```

```php theme={null}
return new UserResource($user->loadCount('posts'));
```

## Resources anidados

Puedes anidar relaciones a través de otra clase resource para mantener una estructura consistente.

```php theme={null}
// app/Http/Resources/PostResource.php
class PostResource extends JsonResource
{
    public function toArray(Request $request): array
    {
        return [
            'id' => $this->id,
            'title' => $this->title,
            'body' => $this->body,
            'author' => new UserResource($this->whenLoaded('user')),
            'comments' => CommentResource::collection($this->whenLoaded('comments')),
            'published_at' => $this->published_at?->toDateString(),
        ];
    }
}
```

```php theme={null}
// Controlador
$post = Post::with(['user', 'comments'])->findOrFail($id);

return new PostResource($post);
```

```json theme={null}
{
  "data": {
    "id": 1,
    "title": "Aprende API resources con Laravel",
    "author": {
      "id": 5,
      "name": "Ana García",
      "email": "ana@example.com"
    },
    "comments": [
      { "id": 10, "body": "¡Muy útil!" }
    ]
  }
}
```

## Añadir metadatos

### `with()`: metadatos de nivel superior

Para añadir metadatos al conjunto de la colección, sobrescribe el método `with()`.

```php theme={null}
class UserCollection extends ResourceCollection
{
    public function toArray(Request $request): array
    {
        return parent::toArray($request);
    }

    public function with(Request $request): array
    {
        return [
            'meta' => [
                'version' => '1.0',
                'generated_at' => now()->toIso8601String(),
            ],
        ];
    }
}
```

Ejemplo de respuesta:

```json theme={null}
{
  "data": [...],
  "meta": {
    "version": "1.0",
    "generated_at": "2024-01-15T10:00:00+00:00"
  }
}
```

### `additional()`: metadatos dinámicos

Para añadir metadatos de forma dinámica desde el controlador utiliza `additional()`.

```php theme={null}
return User::all()
    ->load('roles')
    ->toResourceCollection()
    ->additional(['meta' => [
        'total_admins' => User::where('role', 'admin')->count(),
    ]]);
```

## Integración con la paginación

Basta con pasar el resultado paginado al resource para que se añadan automáticamente `meta` y `links`.

```php theme={null}
Route::get('/users', function () {
    return UserResource::collection(User::paginate(15));
});
```

O bien:

```php theme={null}
return User::paginate(15)->toResourceCollection();
```

Ejemplo de respuesta:

```json theme={null}
{
  "data": [
    { "id": 1, "name": "Ana García" },
    { "id": 2, "name": "Luis Pérez" }
  ],
  "links": {
    "first": "https://example.com/users?page=1",
    "last": "https://example.com/users?page=5",
    "prev": null,
    "next": "https://example.com/users?page=2"
  },
  "meta": {
    "current_page": 1,
    "from": 1,
    "last_page": 5,
    "per_page": 15,
    "to": 15,
    "total": 72
  }
}
```

<Info>
  En las respuestas paginadas la clave `data` se conserva incluso aunque hayas llamado a `withoutWrapping()`, porque debe coexistir con las claves `meta` y `links` de la paginación.
</Info>

## Desactivar el wrapping de datos

Por defecto el resource más externo se envuelve en la clave `data`. Para desactivarlo, invoca `withoutWrapping()` en el método `boot()` de `AppServiceProvider`.

```php theme={null}
use Illuminate\Http\Resources\Json\JsonResource;

public function boot(): void
{
    JsonResource::withoutWrapping();
}
```

<Warning>
  `withoutWrapping()` afecta únicamente al wrapping más externo. Las claves `data` que tú mismo hayas definido no se eliminan.
</Warning>

## Ejemplo práctico: implementación de una API de usuarios

Diseño de respuestas coherentes con resources en una API de gestión de usuarios.

```mermaid theme={null}
flowchart TD
  A["GET /api/users"] --> B["UserController@index"]
  C["GET /api/users/:id"] --> D["UserController@show"]
  B --> E["UserResource::collection(paginate)"]
  D --> F["new UserResource(user)"]
  E --> G["{ data: [...], meta, links }"]
  F --> H["{ data: { id, name, email, ... } }"]
```

### UserResource

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

namespace App\Http\Resources;

use Illuminate\Http\Request;
use Illuminate\Http\Resources\Json\JsonResource;

class UserResource extends JsonResource
{
    public function toArray(Request $request): array
    {
        return [
            'id' => $this->id,
            'name' => $this->name,
            'email' => $this->email,
            'avatar_url' => $this->avatar_url,
            'role' => $this->role,
            // Solo visible para administradores
            'created_at' => $this->when(
                $request->user()?->isAdmin(),
                $this->created_at->toDateString()
            ),
            // Se incluyen solo si están cargados
            'posts' => PostResource::collection($this->whenLoaded('posts')),
            'posts_count' => $this->whenCounted('posts'),
        ];
    }
}
```

### UserController

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

namespace App\Http\Controllers\Api;

use App\Http\Resources\UserResource;
use App\Models\User;
use Illuminate\Http\Request;
use Illuminate\Http\Resources\Json\AnonymousResourceCollection;

class UserController extends Controller
{
    public function index(): AnonymousResourceCollection
    {
        $users = User::withCount('posts')->paginate(20);

        return UserResource::collection($users);
    }

    public function show(User $user): UserResource
    {
        $user->loadCount('posts')->load('posts');

        return new UserResource($user);
    }
}
```

## Páginas relacionadas

<Card title="Introducción a las relaciones de Eloquent" icon="link" href="/es/eloquent-relationships">
  Revisa cómo definir relaciones y aplicar eager loading.
</Card>

<Card title="Paginación" icon="list" href="/es/pagination">
  Consulta cómo combinar los resultados paginados con los API resources.
</Card>


## Related topics

- [Accesores, mutadores y casts de Eloquent](/es/eloquent-mutators.md)
- [Serialización en Eloquent](/es/eloquent-serialization.md)
- [Atributos de PHP](/es/advanced/php-attributes.md)
- [Frontend](/es/frontend.md)
- [Factorías de Eloquent](/es/eloquent-factories.md)
