• Время чтения ~3 мин
  • 08.11.2022

На протяжении многих лет, как разработчики, мы всегда искали способы автоматизации нашей документации, от PHPDoc до Swagger и далее. В последние годы в мире API произошел значительный сдвиг в сторону более ориентированного на дизайн подхода к документации API. В основном это было вызвано созданием спецификации OpenAPI, которая заменила Swagger.

Как разработчики, у нас часто нет времени или желания погружаться в подход, ориентированный на дизайн, к нашим API — это просто факт. Хотя это может быть лучшей практикой, и то, что все говорят. На самом деле нам платят за создание и доставку функций, поэтому мы часто пропускаем этапы. Что, если бы я сказал вам, что вы можете добавить один небольшой шаг в свой рабочий процесс, который позволит вам создавать документацию по API по ходу дела?

Давайте начнем. В этом руководстве я создам совершенно вымышленный API без какой-либо цели, и это будет не то, как я обычно создаю API. Это не просто так, поскольку я хочу, чтобы вы сосредоточились исключительно на шагах, которые я предпринимаю.

Я не буду проходить начальные этапы настройки, так как об этом уже много говорилось в прошлом. В этом уроке я сделаю много предположений — в основном о том, что вы знаете, как настроить и установить Laravel, а также об использовании artisan для создания классов.

У нас есть проект Laravel, открытый в нашей IDE, и нам нужно добавить некоторые функции. Я считаю полезным при создании API оценить назначение API. Постарайтесь понять, зачем он строится и для чего он строится. Это позволяет нам понять, что будет полезно для нашего API, и удерживает нас от добавления дополнительных конечных точек, которые просто не нужны.

We will be building a simple книгиhelf API in this tutorial. It will allow us to create some pretty basic functionality - so that we have a purpose for what we are building. This API aims at consumers who can connect to our API to manage their books. The most important thing to these users is a way to see their books and add books quickly. В конечном итоге будут доступны и другие функции, но всегда начинайте API с основной целью, которая понадобится каждому пользователю.

Создайте новую модель, миграцию и фабрику для модели Book. Это будет основная модель в нашем API. Атрибуты, которые вы хотите использовать в этой модели, не так важны для этого урока, но я все равно пройдусь по ним:

public function up(): void
{
    Schema::create('books', static function (Blueprint $table): void {
        $table->id();
 
        $table->string('title');
        $table->string('subtitle')->nullable();
        $table->text('description');
        $table->string('language');
 
        $table->unsignedBigInteger('pages');
 
        $table->json('authors');
        $table->json('categories');
        $table->json('images');
        $table->json('isbn');
 
        $table->date('published_at');
        $table->timestamps();
    });
}

Это в некоторой степени отражает API Google Книг. У нас есть название книги и подзаголовок, который является названием книги. Описание и язык объясняют, о чем книга и на каком языке. У нас есть подсчет страниц, чтобы увидеть, большая ли это книга. Затем у нас есть авторы, категории, изображения и ISBN для добавления дополнительной информации. Наконец, есть дата публикации.

Теперь, когда у нас есть модель и база данных, мы можем приступить к изучению самого API. Нашим первым шагом будет установка пакета, который позволит нам генерировать нашу документацию по API. Есть отличный пакет под названием Писец который вы можете использовать, который работает как в Laravel, так и в простых приложениях PHP. Вы можете установить его с помощью следующей команды композитора:

composer require --dev knuckleswtf/scribe

Once you have installed this, you can follow the setup instructions to get this working within your application. Once this is installed, and the configuration is published - you can do a test run of the API doc generation to ensure it works as expected. You should get something like this out of the box:

openapi: 3.0.3
info:
  title: Laravel
  description: ''
  version: 1.0.0
servers:
  -
    url: 'http://localhost:8000'
paths: []
tags: []

Теперь, когда мы знаем, что это работает, мы можем добавить несколько маршрутов, чтобы убедиться, что они используются в поколении OpenAPI.

Давайте начнем с наших первых маршрутов по выборке книг, получению списка книг и получению одной книги.

Route::prefix('books')->as('books:')->middleware(['api'])->group(static function (): void {
    Route::get(
        '/',
        App\Http\Controllers\Api\Books\IndexController::class,
    )->name(
        name: 'index',
    );
 
    Route::get(
        '{book}',
        App\Http\Controllers\Api\Books\ShowController::class,
    )->name(
        name: 'show',
    );
});

У нас есть два маршрута под books префикс, и оба ПОЛУЧАТЬ маршруты, не требующие аутентификации. Они станут частью общедоступного API, к которому может получить доступ любой желающий.

Теперь у нас есть маршруты и контроллеры. Нам нужно подумать о том, как мы хотим обрабатывать эти маршруты. Мы хотим сортировать и фильтровать наши запросы, чтобы мы могли запрашивать определенные списки книг. Для этого мы будем использовать Космический построитель запросов Laravel package, обеспечивающий чистый интерфейс для поиска и фильтрации того, что нам нужно. Давайте установим это, используя следующую команду композитора:

composer require spatie/laravel-query-builder

Once this has been installed, we will be able to think about how to filter our API requests to get the correct list of books. With all good APIs, there is pagination. To achieve this, we could use the built-in paginator from Laravel. However, Aaron Francis created a paginator called Fast Paginate which is much more performant - something important when it comes to an API. You can install this using the following composer command:

composer require hammerstone/fast-paginate

Давайте объединим эти две вещи, чтобы мы могли создать коллекцию и вернуть результат с разбивкой на страницы.

return QueryBuilder::for(
    subject: Book::class,
)->allowedFilters(
    filters: ['language', 'pages', 'published_at'],
)->fastPaginate();

Это хорошо работает для нашего варианта использования, однако я не сторонник возврата результатов запроса непосредственно из вашего API. Мы всегда должны преобразовывать ответ, используя ресурс API, в контролируемый формат. Laravel имеет встроенные ресурсы, или вы можете использовать Фрактальные PHP-лиги пакет, что очень хорошо. В этом примере я буду использовать пакет, который у меня есть писалось раньше, поэтому не буду вдаваться в подробности. В итоге у нас должен получиться контроллер, который выглядит следующим образом или, по крайней мере, очень похож на него:

final class IndexController
{
    public function __invoke(Request $request): JsonResponse
    {
        return new JsonResponse(
            data: BookResource::collection(
                resource: QueryBuilder::for(
                    subject: Book::class,
                )->allowedFilters(
                    filters: ['language', 'pages', 'published_at'],
                )->fastPaginate(),
            ),
        );
    }
}

Пока что это только зарегистрирует наш маршрут в спецификации OpenAPI, что лучше, чем ничего. Но с некоторой дополнительной работой мы можем документировать параметры и группировать вещи, что имеет гораздо больше смысла. В Scribe вы можете сделать это с помощью DocBlocks или Attributes. Я предпочитаю использовать для этого DocBlocks, так как нет атрибутов для всех полей, которые вы хотели бы использовать. Позвольте мне показать вам пример, и я покажу вам все, что я сделал.

final class IndexController
{
    /**
     * @group Book Collection
     *
     * Get all Books from the API.
     *
     * @queryParam filter[language] Filter the books to a specific language. filter[language]=en
     * @queryParam filter[pages] Filter the books to those with a certain amount of pages. filter[pages]=1000
     * @queryParam filter[published_at] Filter the books to those published on a certain date. filter[published_at]=12-12-1992
     *
     */
    public function __invoke(Request $request): JsonResponse
    {
        return new JsonResponse(
            data: BookResource::collection(
                resource: QueryBuilder::for(
                    subject: Book::class,
                )->allowedFilters(
                    filters: ['language', 'pages', 'published_at'],
                )->fastPaginate(),
            ),
        );
    }
}

Мы начинаем с добавления Коллекция книг @group который сгруппирует эту конечную точку в «Коллекцию книг», что упростит навигацию по документации вашего API. Затем мы добавляем «Получить все книги из API». который описывает конкретную конечную точку. Затем мы можем добавить дополнительные @queryParam записи для документирования доступных параметров запроса, которые принимает эта конечная точка. Гораздо проще, чем писать YAML, верно!

Документация Scribe содержит гораздо больше информации, и вы можете подробно изучить, какую информацию вы добавляете. Здесь я коснулся только основ, но вы уже видите, насколько это может быть полезно. Что вы используете для своей документации по API? Дайте нам знать в Твиттере!

Comments

No comments yet
Yurij Finiv

Yurij Finiv

Full stack

Про мене

Professional Fullstack Developer with extensive experience in website and desktop application development. Proficient in a wide range of tools and technologies, including Bootstrap, Tailwind, HTML5, CSS3, PUG, JavaScript, Alpine.js, jQuery, PHP, MODX, and Node.js. Skilled in website development using Symfony, MODX, and Laravel. Experience: Contributed to the development and translation of MODX3 i...

Об авторе CrazyBoy49z
WORK EXPERIENCE
Контакты
Ukraine, Lutsk
+380979856297