Повернути модель з контролера можна - Laravel серіалізує її в JSON автоматично:
return $user; // усі атрибути моделі (крім $hidden)
Але так формат відповіді API = структура таблиці. Додали колонку - вона з'явилася в API. Перейменували - зламали клієнтів. Внутрішнє поле (прапорець, службова дата) - уже публічне.
API Resource - окремий шар, що явно описує, як модель виглядає назовні:
php artisan make:resource UserResource
class UserResource extends JsonResource
{
public function toArray(Request $request): array
{
return [
'id' => $this->id,
'name' => $this->name,
'avatar_url' => $this->avatarUrl(),
'registered_at' => $this->created_at,
'posts_count' => $this->whenCounted('posts'),
'email' => $this->when($request->user()?->is($this->resource), $this->email),
];
}
}
return new UserResource($user);
return UserResource::collection($users);
return $user->toResource(); // те саме, коротше
Що дає ресурс:
- контракт API відокремлено від бази: перейменування колонки змінює лише ресурс, а не відповідь;
- явний перелік полів: нове поле в таблиці не з'явиться в API випадково;
- обчислювані поля (URL, форматування) і умовні поля за правами;
- вкладені ресурси й зв'язки - лише якщо вони завантажені (
whenLoaded); - обгортка
data, пагінація зlinksіmeta- автоматично для колекцій.
Обгортка data: відповідь має вигляд { "data": { ... } }. Вимкнути - JsonResource::withoutWrapping() у сервіс-провайдері, але для колекцій з пагінацією обгортка корисна (там же meta).
JSON:API. Якщо потрібен стандартний формат специфікації JSON:API (type, id, attributes, relationships, included), у Laravel 13 є вбудований JsonApiResource - php artisan make:resource PostResource --json-api.
Пастка: ресурс не захищає від N+1. Якщо в toArray звертатися до незавантажених зв'язків ($this->author->name), кожен елемент колекції - окремий запит. Звідси whenLoaded і with() у контролері.