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.
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.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.
Puedes devolver el resource definido desde un controlador o una ruta.
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.
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.
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().
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.
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.
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.
// Con los posts incluidosreturn new UserResource($user->load('posts'));// Sin los postsreturn new UserResource($user);
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.
Por defecto el resource más externo se envuelve en la clave data. Para desactivarlo, invoca withoutWrapping() en el método boot() de AppServiceProvider.
use Illuminate\Http\Resources\Json\JsonResource;public function boot(): void{ JsonResource::withoutWrapping();}
withoutWrapping() afecta únicamente al wrapping más externo. Las claves data que tú mismo hayas definido no se eliminan.