Estamos actualizando el sitio. Durante unos días es posible que veas fallos de diseño o de traducción. La documentación sigue disponible: si una página se ve mal, recárgala más tarde.

Inicio Laravel 10.x Eloquent: Recursos API

Eloquent: Recursos API

10.x 7 de mar. de 2026

#Introducción

Al construir una API, puede necesitar una capa de transformación que se sitúe entre sus modelos Eloquent y las respuestas JSON que realmente se devuelven a los usuarios de su aplicación. Por ejemplo, puede que desee mostrar ciertos atributos para un subconjunto de usuarios y no para otros, o puede que siempre quiera incluir ciertas relaciones en la representación JSON de sus modelos. Las clases de recursos de Eloquent le permiten transformar sus modelos y colecciones de modelos en JSON de forma expresiva y sencilla.

Por supuesto, siempre puede convertir modelos o colecciones Eloquent a JSON usando sus métodos toJson; sin embargo, los recursos de Eloquent proporcionan un control más granular y robusto sobre la serialización JSON de sus modelos y sus relaciones.

#Generación de recursos

Para generar una clase de recurso, puede usar el comando Artisan make:resource. Por defecto, los recursos se colocarán en el directorio app/Http/Resources de su aplicación. Los recursos extienden la clase Illuminate\Http\Resources\Json\JsonResource:

php artisan make:resource UserResource

#Colecciones de recursos

Además de generar recursos que transforman modelos individuales, puede generar recursos responsables de transformar colecciones de modelos. Esto permite que sus respuestas JSON incluyan enlaces y otra información meta relevante para toda una colección de un recurso dado.

Para crear una colección de recursos, debe usar la bandera --collection al crear el recurso. O bien, incluir la palabra Collection en el nombre del recurso indicará a Laravel que debe crear un recurso de colección. Los recursos de colección extienden la clase Illuminate\Http\Resources\Json\ResourceCollection:

php artisan make:resource User --collection

php artisan make:resource UserCollection

#Descripción general del concepto

Примечание

Esta es una visión general de alto nivel sobre recursos y colecciones de recursos. Se recomienda encarecidamente leer las otras secciones de esta documentación para obtener una comprensión más profunda de la personalización y el poder que los recursos le ofrecen.

Antes de profundizar en todas las opciones disponibles al escribir recursos, veamos primero a alto nivel cómo se usan los recursos dentro de Laravel. Una clase de recurso representa un solo modelo que necesita ser transformado en una estructura JSON. Por ejemplo, aquí hay una clase de recurso simple UserResource:

<?php

namespace App\Http\Resources;

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

class UserResource extends JsonResource
{
    /**
     * Transformar el recurso en un array.
     *
     * @return array<string, mixed>
     */
    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,
        ];
    }
}

Cada clase de recurso define un método toArray que devuelve el array de atributos que deben convertirse a JSON cuando el recurso se devuelve como respuesta desde una ruta o método de controlador.

Tenga en cuenta que podemos acceder a las propiedades del modelo directamente desde la variable $this. Esto se debe a que una clase de recurso automáticamente delega el acceso a propiedades y métodos al modelo subyacente para un acceso conveniente. Una vez definido el recurso, puede ser devuelto desde una ruta o controlador. El recurso acepta la instancia del modelo subyacente a través de su constructor:

use App\Http\Resources\UserResource;
use App\Models\User;

Route::get('/user/{id}', function (string $id) {
    return new UserResource(User::findOrFail($id));
});

#Colecciones de recursos

Si está devolviendo una colección de recursos o una respuesta paginada, debe usar el método collection proporcionado por su clase de recurso al crear la instancia del recurso en su ruta o controlador:

use App\Http\Resources\UserResource;
use App\Models\User;

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

Tenga en cuenta que esto no permite añadir metadatos personalizados que puedan necesitar devolverse con su colección. Si desea personalizar la respuesta de la colección de recursos, puede crear un recurso dedicado para representar la colección:

php artisan make:resource UserCollection

Una vez generada la clase de colección de recursos, puede definir fácilmente cualquier metadato que deba incluirse con la respuesta:

<?php

namespace App\Http\Resources;

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

class UserCollection extends ResourceCollection
{
    /**
     * Transformar la colección de recursos en un array.
     *
     * @return array<int|string, mixed>
     */
    public function toArray(Request $request): array
    {
        return [
            'data' => $this->collection,
            'links' => [
                'self' => 'link-value',
            ],
        ];
    }
}

Después de definir su colección de recursos, puede ser devuelta desde una ruta o controlador:

use App\Http\Resources\UserCollection;
use App\Models\User;

Route::get('/users', function () {
    return new UserCollection(User::all());
});

#Preservando las claves de la colección

Al devolver una colección de recursos desde una ruta, Laravel reinicia las claves de la colección para que estén en orden numérico. Sin embargo, puede añadir una propiedad preserveKeys a su clase de recurso indicando si se deben preservar las claves originales de la colección:

<?php

namespace App\Http\Resources;

use Illuminate\Http\Resources\Json\JsonResource;

class UserResource extends JsonResource
{
    /**
     * Indica si se deben preservar las claves de la colección del recurso.
     *
     * @var bool
     */
    public $preserveKeys = true;
}

Cuando la propiedad preserveKeys está establecida en true, las claves de la colección se preservarán cuando la colección sea devuelta desde una ruta o controlador:

use App\Http\Resources\UserResource;
use App\Models\User;

Route::get('/users', function () {
    return UserResource::collection(User::all()->keyBy->id);
});

#Personalizando la clase de recurso subyacente

Normalmente, la propiedad $this->collection de una colección de recursos se llena automáticamente con el resultado de mapear cada elemento de la colección a su clase de recurso singular. Se asume que la clase de recurso singular es el nombre de la clase de la colección sin la parte final Collection. Además, dependiendo de su preferencia personal, la clase de recurso singular puede o no tener el sufijo Resource.

Por ejemplo, UserCollection intentará mapear las instancias de usuario dadas en el recurso UserResource. Para personalizar este comportamiento, puede sobrescribir la propiedad $collects de su colección de recursos:

<?php

namespace App\Http\Resources;

use Illuminate\Http\Resources\Json\ResourceCollection;

class UserCollection extends ResourceCollection
{
    /**
     * El recurso que esta colección recoge.
     *
     * @var string
     */
    public $collects = Member::class;
}

#Escribiendo recursos

Примечание

Si no ha leído la descripción general del concepto, se recomienda encarecidamente hacerlo antes de continuar con esta documentación.

Los recursos solo necesitan transformar un modelo dado en un array. Por lo tanto, cada recurso contiene un método toArray que traduce los atributos de su modelo en un array amigable para la API que puede ser devuelto desde las rutas o controladores de su aplicación:

<?php

namespace App\Http\Resources;

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

class UserResource extends JsonResource
{
    /**
     * Transformar el recurso en un array.
     *
     * @return array<string, mixed>
     */
    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,
        ];
    }
}

Una vez definido un recurso, puede ser devuelto directamente desde una ruta o controlador:

use App\Http\Resources\UserResource;
use App\Models\User;

Route::get('/user/{id}', function (string $id) {
    return new UserResource(User::findOrFail($id));
});

#Relaciones

Si desea incluir recursos relacionados en su respuesta, puede agregarlos al array devuelto por el método toArray de su recurso. En este ejemplo, usaremos el método collection del recurso PostResource para añadir las publicaciones del blog del usuario a la respuesta del recurso:

use App\Http\Resources\PostResource;
use Illuminate\Http\Request;

/**
 * Transformar el recurso en un array.
 *
 * @return array<string, mixed>
 */
public function toArray(Request $request): array
{
    return [
        'id' => $this->id,
        'name' => $this->name,
        'email' => $this->email,
        'posts' => PostResource::collection($this->posts),
        'created_at' => $this->created_at,
        'updated_at' => $this->updated_at,
    ];
}
Примечание

Si desea incluir relaciones solo cuando ya han sido cargadas, consulte la documentación sobre relaciones condicionales.

#Colecciones de recursos

Mientras que los recursos transforman un solo modelo en un array, las colecciones de recursos transforman una colección de modelos en un array. Sin embargo, no es absolutamente necesario definir una clase de colección de recursos para cada uno de sus modelos, ya que todos los recursos proporcionan un método collection para generar una colección de recursos "ad-hoc" sobre la marcha:

use App\Http\Resources\UserResource;
use App\Models\User;

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

Sin embargo, si necesita personalizar los metadatos devueltos con la colección, es necesario definir su propia colección de recursos:

<?php

namespace App\Http\Resources;

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

class UserCollection extends ResourceCollection
{
    /**
     * Transformar la colección de recursos en un array.
     *
     * @return array<string, mixed>
     */
    public function toArray(Request $request): array
    {
        return [
            'data' => $this->collection,
            'links' => [
                'self' => 'link-value',
            ],
        ];
    }
}

Al igual que los recursos singulares, las colecciones de recursos pueden ser devueltas directamente desde rutas o controladores:

use App\Http\Resources\UserCollection;
use App\Models\User;

Route::get('/users', function () {
    return new UserCollection(User::all());
});

#Envolviendo datos

Por defecto, su recurso más externo está envuelto en una clave data cuando la respuesta del recurso se convierte a JSON. Por ejemplo, una respuesta típica de colección de recursos se ve así:

{
    "data": [
        {
            "id": 1,
            "name": "Eladio Schroeder Sr.",
            "email": "therese28@example.com"
        },
        {
            "id": 2,
            "name": "Liliana Mayert",
            "email": "evandervort@example.com"
        }
    ]
}

Si desea deshabilitar el envoltorio del recurso más externo, debe invocar el método withoutWrapping en la clase base Illuminate\Http\Resources\Json\JsonResource. Normalmente, debería llamar a este método desde su AppServiceProvider u otro service provider que se cargue en cada solicitud a su aplicación:

<?php

namespace App\Providers;

use Illuminate\Http\Resources\Json\JsonResource;
use Illuminate\Support\ServiceProvider;

class AppServiceProvider extends ServiceProvider
{
    /**
     * Registrar cualquier servicio de la aplicación.
     */
    public function register(): void
    {
        // ...
    }

    /**
     * Inicializar cualquier servicio de la aplicación.
     */
    public function boot(): void
    {
        JsonResource::withoutWrapping();
    }
}
Внимание

El método withoutWrapping solo afecta la respuesta más externa y no eliminará las claves data que usted agregue manualmente a sus propias colecciones de recursos.

#Envolviendo recursos anidados

Tiene total libertad para determinar cómo se envuelven las relaciones de su recurso. Si desea que todas las colecciones de recursos estén envueltas en una clave data, independientemente de su anidamiento, debe definir una clase de colección de recursos para cada recurso y devolver la colección dentro de una clave data.

Puede preguntarse si esto hará que su recurso más externo esté envuelto en dos claves data. No se preocupe, Laravel nunca permitirá que sus recursos se envuelvan accidentalmente dos veces, por lo que no debe preocuparse por el nivel de anidamiento de la colección de recursos que está transformando:

<?php

namespace App\Http\Resources;

use Illuminate\Http\Resources\Json\ResourceCollection;

class CommentsCollection extends ResourceCollection
{
    /**
     * Transformar la colección de recursos en un array.
     *
     * @return array<string, mixed>
     */
    public function toArray(Request $request): array
    {
        return ['data' => $this->collection];
    }
}

#Envolviendo datos y paginación

Al devolver colecciones paginadas mediante una respuesta de recurso, Laravel envolverá sus datos de recurso en una clave data incluso si se ha llamado al método withoutWrapping. Esto se debe a que las respuestas paginadas siempre contienen claves meta y links con información sobre el estado del paginador:

{
    "data": [
        {
            "id": 1,
            "name": "Eladio Schroeder Sr.",
            "email": "therese28@example.com"
        },
        {
            "id": 2,
            "name": "Liliana Mayert",
            "email": "evandervort@example.com"
        }
    ],
    "links":{
        "first": "http://example.com/users?page=1",
        "last": "http://example.com/users?page=1",
        "prev": null,
        "next": null
    },
    "meta":{
        "current_page": 1,
        "from": 1,
        "last_page": 1,
        "path": "http://example.com/users",
        "per_page": 15,
        "to": 10,
        "total": 10
    }
}

#Paginación

Puede pasar una instancia de paginador de Laravel al método collection de un recurso o a una colección de recursos personalizada:

use App\Http\Resources\UserCollection;
use App\Models\User;

Route::get('/users', function () {
    return new UserCollection(User::paginate());
});

Las respuestas paginadas siempre contienen claves meta y links con información sobre el estado del paginador:

{
    "data": [
        {
            "id": 1,
            "name": "Eladio Schroeder Sr.",
            "email": "therese28@example.com"
        },
        {
            "id": 2,
            "name": "Liliana Mayert",
            "email": "evandervort@example.com"
        }
    ],
    "links":{
        "first": "http://example.com/users?page=1",
        "last": "http://example.com/users?page=1",
        "prev": null,
        "next": null
    },
    "meta":{
        "current_page": 1,
        "from": 1,
        "last_page": 1,
        "path": "http://example.com/users",
        "per_page": 15,
        "to": 10,
        "total": 10
    }
}

#Personalizando la información de paginación

Si desea personalizar la información incluida en las claves links o meta de la respuesta de paginación, puede definir un método paginationInformation en el recurso. Este método recibirá los datos $paginated y el array de información $default, que es un array que contiene las claves links y meta:

/**
 * Personalizar la información de paginación para el recurso.
 *
 * @param  \Illuminate\Http\Request  $request
 * @param  array $paginated
 * @param  array $default
 * @return array
 */
public function paginationInformation($request, $paginated, $default)
{
    $default['links']['custom'] = 'https://example.com';

    return $default;
}

#Atributos condicionales

A veces puede querer incluir un atributo en la respuesta del recurso solo si se cumple una condición dada. Por ejemplo, puede querer incluir un valor solo si el usuario actual es un "administrador". Laravel proporciona varios métodos auxiliares para ayudarle en esta situación. El método when puede usarse para añadir condicionalmente un atributo a la respuesta del recurso:

/**
 * Transform the resource into an array.
 *
 * @return array<string, mixed>
 */
public function toArray(Request $request): array
{
    return [
        'id' => $this->id,
        'name' => $this->name,
        'email' => $this->email,
        'secret' => $this->when($request->user()->isAdmin(), 'secret-value'),
        'created_at' => $this->created_at,
        'updated_at' => $this->updated_at,
    ];
}

En este ejemplo, la clave secret solo se devolverá en la respuesta final del recurso si el método isAdmin del usuario autenticado devuelve true. Si el método devuelve false, la clave secret será eliminada de la respuesta del recurso antes de enviarse al cliente. El método when le permite definir sus recursos de forma expresiva sin recurrir a sentencias condicionales al construir el array.

El método when también acepta un closure como segundo argumento, permitiéndole calcular el valor resultante solo si la condición dada es true:

'secret' => $this->when($request->user()->isAdmin(), function () {
    return 'secret-value';
}),

El método whenHas puede usarse para incluir un atributo si está realmente presente en el modelo subyacente:

'name' => $this->whenHas('name'),

Además, el método whenNotNull puede usarse para incluir un atributo en la respuesta del recurso si el atributo no es nulo:

'name' => $this->whenNotNull($this->name),

#Fusionando atributos condicionales

A veces puede tener varios atributos que solo deben incluirse en la respuesta del recurso basándose en la misma condición. En este caso, puede usar el método mergeWhen para incluir los atributos en la respuesta solo cuando la condición dada sea true:

/**
 * Transformar el recurso en un array.
 *
 * @return array<string, mixed>
 */
public function toArray(Request $request): array
{
    return [
        'id' => $this->id,
        'name' => $this->name,
        'email' => $this->email,
        $this->mergeWhen($request->user()->isAdmin(), [
            'first-secret' => 'value',
            'second-secret' => 'value',
        ]),
        'created_at' => $this->created_at,
        'updated_at' => $this->updated_at,
    ];
}

De nuevo, si la condición dada es false, estos atributos serán eliminados de la respuesta del recurso antes de enviarse al cliente.

Внимание

El método mergeWhen no debe usarse dentro de arrays que mezclen claves string y numéricas. Además, no debe usarse dentro de arrays con claves numéricas que no estén ordenadas secuencialmente.

#Relaciones condicionales

Además de cargar atributos condicionalmente, puede incluir relaciones condicionalmente en sus respuestas de recursos basándose en si la relación ya ha sido cargada en el modelo. Esto permite que su controlador decida qué relaciones deben cargarse en el modelo y su recurso puede incluirlas fácilmente solo cuando realmente han sido cargadas. En última instancia, esto facilita evitar problemas de consultas "N+1" dentro de sus recursos.

El método whenLoaded puede usarse para cargar condicionalmente una relación. Para evitar cargar relaciones innecesariamente, este método acepta el nombre de la relación en lugar de la relación misma:

use App\Http\Resources\PostResource;

/**
 * Transformar el recurso en un array.
 *
 * @return array<string, mixed>
 */
public function toArray(Request $request): array
{
    return [
        'id' => $this->id,
        'name' => $this->name,
        'email' => $this->email,
        'posts' => PostResource::collection($this->whenLoaded('posts')),
        'created_at' => $this->created_at,
        'updated_at' => $this->updated_at,
    ];
}

En este ejemplo, si la relación no ha sido cargada, la clave posts será eliminada de la respuesta del recurso antes de enviarse al cliente.

#Conteos condicionales de relaciones

Además de incluir relaciones condicionalmente, puede incluir condicionalmente los "conteos" de relaciones en sus respuestas de recursos basándose en si el conteo de la relación ha sido cargado en el modelo:

new UserResource($user->loadCount('posts'));

El método whenCounted puede usarse para incluir condicionalmente el conteo de una relación en su respuesta de recurso. Este método evita incluir innecesariamente el atributo si el conteo de la relación no está presente:

/**
 * Transformar el recurso en un array.
 *
 * @return array<string, mixed>
 */
public function toArray(Request $request): array
{
    return [
        'id' => $this->id,
        'name' => $this->name,
        'email' => $this->email,
        'posts_count' => $this->whenCounted('posts'),
        'created_at' => $this->created_at,
        'updated_at' => $this->updated_at,
    ];
}

En este ejemplo, si el conteo de la relación posts no ha sido cargado, la clave posts_count será eliminada de la respuesta del recurso antes de enviarse al cliente.

Otros tipos de agregados, como avg, sum, min y max también pueden cargarse condicionalmente usando el método whenAggregated:

'words_avg' => $this->whenAggregated('posts', 'words', 'avg'),
'words_sum' => $this->whenAggregated('posts', 'words', 'sum'),
'words_min' => $this->whenAggregated('posts', 'words', 'min'),
'words_max' => $this->whenAggregated('posts', 'words', 'max'),

#Información condicional del pivot

Además de incluir condicionalmente información de relaciones en sus respuestas de recursos, puede incluir condicionalmente datos de las tablas intermedias de relaciones muchos a muchos usando el método whenPivotLoaded. El método whenPivotLoaded acepta el nombre de la tabla pivot como primer argumento. El segundo argumento debe ser un closure que devuelve el valor a retornar si la información del pivot está disponible en el modelo:

/**
 * Transformar el recurso en un array.
 *
 * @return array<string, mixed>
 */
public function toArray(Request $request): array
{
    return [
        'id' => $this->id,
        'name' => $this->name,
        'expires_at' => $this->whenPivotLoaded('role_user', function () {
            return $this->pivot->expires_at;
        }),
    ];
}

Si su relación está usando un modelo intermedio personalizado, puede pasar una instancia del modelo intermedio como primer argumento al método whenPivotLoaded:

'expires_at' => $this->whenPivotLoaded(new Membership, function () {
    return $this->pivot->expires_at;
}),

Si su tabla intermedia está usando un accesor distinto a pivot, puede usar el método whenPivotLoadedAs:

/**
 * Transformar el recurso en un array.
 *
 * @return array<string, mixed>
 */
public function toArray(Request $request): array
{
    return [
        'id' => $this->id,
        'name' => $this->name,
        'expires_at' => $this->whenPivotLoadedAs('subscription', 'role_user', function () {
            return $this->subscription->expires_at;
        }),
    ];
}

#Añadiendo metadatos

Algunos estándares JSON API requieren la adición de metadatos a sus respuestas de recursos y colecciones de recursos. Esto a menudo incluye cosas como links al recurso o recursos relacionados, o metadatos sobre el recurso mismo. Si necesita devolver metadatos adicionales sobre un recurso, inclúyalos en su método toArray. Por ejemplo, podría incluir información de link al transformar una colección de recursos:

/**
 * Transformar el recurso en un array.
 *
 * @return array<string, mixed>
 */
public function toArray(Request $request): array
{
    return [
        'data' => $this->collection,
        'links' => [
            'self' => 'link-value',
        ],
    ];
}

Al devolver metadatos adicionales desde sus recursos, nunca tendrá que preocuparse por sobrescribir accidentalmente las claves links o meta que Laravel añade automáticamente al devolver respuestas paginadas. Cualquier links adicional que defina se fusionará con los enlaces proporcionados por el paginador.

#Metadatos de nivel superior

A veces puede querer incluir ciertos metadatos con una respuesta de recurso solo si el recurso es el recurso más externo que se está devolviendo. Normalmente, esto incluye información meta sobre la respuesta en su conjunto. Para definir estos metadatos, añada un método with a su clase de recurso. Este método debe devolver un array de metadatos que se incluirán con la respuesta del recurso solo cuando el recurso sea el recurso más externo que se está transformando:

<?php

namespace App\Http\Resources;

use Illuminate\Http\Resources\Json\ResourceCollection;

class UserCollection extends ResourceCollection
{
    /**
     * Transformar la colección de recursos en un array.
     *
     * @return array<string, mixed>
     */
    public function toArray(Request $request): array
    {
        return parent::toArray($request);
    }

    /**
     * Obtener datos adicionales que deben devolverse con el array del recurso.
     *
     * @return array<string, mixed>
     */
    public function with(Request $request): array
    {
        return [
            'meta' => [
                'key' => 'value',
            ],
        ];
    }
}

#Añadiendo metadatos al construir recursos

También puede añadir datos de nivel superior al construir instancias de recursos en su ruta o controlador. El método additional, que está disponible en todos los recursos, acepta un array de datos que deben añadirse a la respuesta del recurso:

return (new UserCollection(User::all()->load('roles')))
                ->additional(['meta' => [
                    'key' => 'value',
                ]]);

#Respuestas de recursos

Como ya ha leído, los recursos pueden ser devueltos directamente desde rutas y controladores:

use App\Http\Resources\UserResource;
use App\Models\User;

Route::get('/user/{id}', function (string $id) {
    return new UserResource(User::findOrFail($id));
});

Sin embargo, a veces puede necesitar personalizar la respuesta HTTP saliente antes de enviarla al cliente. Hay dos formas de lograr esto. Primero, puede encadenar el método response al recurso. Este método devolverá una instancia de Illuminate\Http\JsonResponse, dándole control total sobre los encabezados de la respuesta:

use App\Http\Resources\UserResource;
use App\Models\User;

Route::get('/user', function () {
    return (new UserResource(User::find(1)))
                ->response()
                ->header('X-Value', 'True');
});

Alternativamente, puede definir un método withResponse dentro del recurso mismo. Este método será llamado cuando el recurso se devuelva como el recurso más externo en una respuesta:

<?php

namespace App\Http\Resources;

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

class UserResource extends JsonResource
{
    /**
     * Transformar el recurso en un array.
     *
     * @return array<string, mixed>
     */
    public function toArray(Request $request): array
    {
        return [
            'id' => $this->id,
        ];
    }

    /**
     * Personalizar la respuesta saliente para el recurso.
     */
    public function withResponse(Request $request, JsonResponse $response): void
    {
        $response->header('X-Value', 'True');
    }
}