Ресурсы для API в Laravel

Вы узнаете, что такое Ресурсы и как их использовать для API в Laravel-приложениях.

Ресурсы (Resources) — дополнительный слой поверх Eloquent-моделей. Они позволяют поддерживать единый формат возвращаемых данных. Плюс позволяют отсекать лишние или секретные данные, например, пароли и токены.

Настройка проекта

Создадим новый Laravel-проект с пользователями. Откроем миграцию database/migrations/2014_10_12_000000_create_users_table.php и добавим в Schema::create новое поле с возрастом:

$table->integer('age');

Откроем фабрику database/factories/UserFactory.php и также добавим в него поле возраста:

public function definition()
{
    return [
        'name' => $this->faker->name(),
        'email' => $this->faker->unique()->safeEmail(),
        'email_verified_at' => now(),
        'password' => '$2y$10$92IXUNpkjO0rOQ5byMi.Ye4oKoEa3Ro9llC/.og/at2.uheWG/igi',
        'remember_token' => Str::random(10),
        'age' => $this->faker->numberBetween(10, 90)
    ];
}

Откроем пользовательскую модель app/Models/User.php и добавим возраст в массив $fillable:

protected $fillable = [
    'name',
    'email',
    'password',
    'age'
];

Выполним миграции:

php artisan migrate

База данных готова. Займемся её наполнением. Открываем database/seeders/DatabaseSeeder.php и раскомментируем (убираем два слэша в начале) следующую строку:

// \App\Models\User::factory(10)->create();

Выполняем посев (наполнение данными):

php artisan db:seed

Получаем 10 фейковых пользователей.

Создание Ресурса

Ресурсы создаются поверх eloquent-моделей. То есть для каждой модели вам нужен отдельный ресурс.

Создадим Ресурс для модели User и посмотрим как его можно использовать для запросов.

Ресурс создается следующей artisan-командой:

php artisan make:resource UserResource

Она создаст новый класс UserResource в папке app/Http/Resources. В этой папке будут находится все создаваемые Ресурсы.

Открываем файл UserResource и видим в нём метод toArray. Он используется для возврата данных пользователя. По умолчанию просто возвращается результат этого метода.

Текущая модель, для которой используется Ресурс, представлена внутри класса полем resource. Соответственно, вы можете ссылаться на неё с помощью $this->resource.

Изменим возвращаемый массив, чтобы он возвращал только идентификатор, имя и почту пользователя. Заменим содержимое метода toArray на следующее:

return [
    'id' => $this->resource->id,
    'name' => $this->resource->name,
    'email' => $this->resource->email
];

Готово, мы создали наш первый Ресурс!

Создаем конечную точку API

Создадим эндпоинт, который позволит получать данные любого пользователя по его идентификатору. Но сначала создадим контроллер:

php artisan make:controller UserController

Откроем свежесозданный app/Http/Controllers/UserController.php и создадим в нём новый метод getUser, который будет принимать идентификатор и возвращать данные пользователя.

public function getUser($id) {
    $user = User::query()->find($id);

    if (!$user) {
        return response()->json(['success' => false, 'message' => 'User does not exist']);
    }

    return response()->json(['success' => true, 'user' => new UserResource($user)]);
}

Сначала он проверяет, существует ли пользователь с таким идентификатором. Если нет, то возвращаем json с ошибкой. Если существует, то возвращаем экземпляр UserResource.

Обратите внимание, что для создания экземпляра Ресурса вам необходимо передать ему модель. Например так:

new UserResource($user)

Это все! Осталось добавить конечную точку в маршруты.

Открываем routes/api.php и добавляем:

Route::get('/user/{id}', [UserController::class, 'getUser']);

Наш API готов. Пора его проверить.

Запускаем сервер:

php artisan serve

Для тестирования API удобнее использовать что-то вроде Postman или Thunder Client. Но пока нам нужен обычнй GET-запрос без авторизации, то просто откроем адрес в браузере: localhost:8000/api/user/1

Видим, что нам пришёл json с двумя ключами success и user. Обратите внимание что объект user содержит только id, name и email, то есть именно те поля, которые мы настроили в нашем Ресурсе

.

Вы можете попробовать менять идентификатор и увидите, что API придерживается единой структуры ответа для всех пользователей.

Ресурс коллекции пользователей

Ресурсы предназначены не только для экземпляров моделей. Вы можете создать ресурс коллекции, который принимает в качестве параметра коллекции экземпляров моделей и, при необходимости, изменяет её.

Чтобы создать Ресурс коллекции для модели User выполним следующую команду:

php artisan make:resource UserCollection --collection

Будет создан файл app/Http/Resources/UserCollection.php.

Обратите внимание, что для Ресурсов коллекций существует соглашение об именах <Model>Collection, которое гарантирует, что коллекция связана с правильной моделью.

Итак, поскольку название модели — User, то название Ресурса коллекции будет UserCollection. Если хотите назвать коллекцию по другому, то можете указать класс ресурса, используя поле $collects в классе коллекции.

public $collects = User::class;

Ресурсы коллекций аналогичны обычным Ресурсам. Также есть метод toArray, который используется для задания структуры коллекций.

Изменим его содержание на следующее:

$count = $this->collection->count();

return [
    'count' => $count,
    'data' => $this->collection
];

Обратите внимание, что $this->collection вернет массив объектов со структурой, аналогичной заданной в UserResource, для каждого пользователя в коллекции.

Создаем конечную точку API

Создадим эндпоинт, которая позволит нам получить всех пользователей. Добавим в UserController следующий метод:

public function getUsers () {
    $users = User::query()->get();

    return response()->json(['success' => true, 'users' => new UserCollection($users)]);
}

Для создания экземпляр ресурса коллекции также необходимо передать коллекцию моделей в качестве параметра.

Добавим новый маршрут в routes/api.php:

Route::get('/user', [UserController::class, 'getUsers']);

Давайте его проверим. Открываем адрес localhost:8000/api/user в браузере. Он вернет два ключа success и users, который содержит массив всех пользователей и все они будут иметь структуру, заданную наши в UserResource.

Это пример того, как заданный Ресурс гарантирует одинаковую структуру для всех запросов.

Условные поля

Иногда, в зависимости от условий, вам нужно будет добавить дополнительное поле в ответ. Для этого можно использовать метод when.

Предположим, что нам нужно возвращать возраст пользователя, если ему больше 18 лет. Добавим следующую строку в возвращаемый массив toArray в UserResource:

'old' => $this->when($this->resource->age > 18, true)

Теперь, если пользователь старше 18 лет, то в ответ будет добавлено полей age. Обратите внимание, что первый параметр это условие, а второй — ожидаемое значение.

Откроем адрес localhost:8000/api/user в браузере. Теперь у взрослых пользователей будет поле возраста, а у детей — нет.

Обратите внимание, что в вашем случае результат будет другой, так как фабрика генерирует пользователей случайным образом.

Заключение

Ресурсы позволяют структурировать данные выдаваемых в запросах и гарантировать сокрытие небезопасных данных.

Вы можете также почитать о Ресурсах в документации Laravel.

Код из статьи доступен на Github.

Автор: Shahed Nasser
Перевод: Алексей Широков

Наш Телеграм-канал — следите за новостями о Laravel.