OpenApi bake theme pluginをswagger-phpのアトリビュート形式で吐き出すように修正しました

最近は、アプリの開発・実行基盤の構築でインフラエンジニアっぽいことばかりやっていて、プロダクションのコードを書いていなくてストレスが溜まりつつある渡辺です

今構築している環境では、PHPAPIを書いていて、swagger-phpAPIドキュメントを作成して、フロントアプリとの連携をしています。以前書いたブログで OpenApi bake theme plugin や swagger-phpを紹介しているので参考にしてみてください。

kaz29.hatenablog.com

kaz29.hatenablog.com

当初、OpenApi bake theme pluginを使ってアノテーション形式で記載しようと思っていたのですが、構築するにあたりswagger-phpの開発状況を確認したところ、かなりアップデートされていて、より便利に使えそうなのでほとんどメンテナンスしていなかった、 OpenApi bake theme plugin を修正することにしました。

OpenApiTheme plugin for CakePHP

swagger-php

swagger-phpで利用可能な形式は元々はアノテーション形式のみだったのですが、アトリビュート形式がいつの間にか追加されていました。以下がそれぞれのサンプルです。

/**
 * Article Entity
 *
 * @OA\Schema(
 *      schema="Article",
 *      title="",
 *      description="Article entity",
 *       @OA\Property(
 *           property="id",
 *           type="integer",
 *           format="int32",
 *           description="",
 *       ),
 *       @OA\Property(
 *           property="user_id",
 *           type="integer",
 *           format="int32",
 *           description="",
 *       ),
 *       @OA\Property(
 *           property="title",
 *           type="string",
 *           description="",
 *       ),
 *       @OA\Property(
 *           property="slug",
 *           type="string",
 *           description="",
 *       ),
 *       @OA\Property(
 *           property="body",
 *           type="string",
 *           description="",
 *       ),
 *       @OA\Property(
 *           property="published",
 *           type="boolean",
 *           description="",
 *       ),
 *       @OA\Property(
 *           property="created",
 *           type="string",
 *           format="datetime",
 *           description="",
 *       ),
 *       @OA\Property(
 *           property="modified",
 *           type="string",
 *           format="datetime",
 *           description="",
 *       ),
 * )
#[OA\Schema(
    schema: 'Article',
    title: 'Article',
    description: 'Article Entity',
    required: ['id', 'user_id', 'title', 'slug', 'published', 'user', 'tags'],
    properties: [
        new OA\Property(
            property: 'id',
            type: 'integer',
            format: 'int32',
            description: '',
        ),
        new OA\Property(
            property: 'user_id',
            type: 'integer',
            format: 'int32',
            description: '',
        ),
        new OA\Property(
            property: 'title',
            type: 'string',
            description: '',
        ),
        new OA\Property(
            property: 'slug',
            type: 'string',
            description: '',
        ),
        new OA\Property(
            property: 'body',
            type: 'string',
            description: '',
        ),
        new OA\Property(
            property: 'published',
            type: 'boolean',
            description: '',
        ),
        new OA\Property(
            property: 'created',
            type: 'string',
            format: 'datetime',
            description: '',
        ),
        new OA\Property(
            property: 'modified',
            type: 'string',
            format: 'datetime',
            description: '',
        ),
    ]
)]

アノテーションでの記述でも便利なのですが、所詮コメントの中に記載する形なので、書き心地はあまり良いとはいえない状況でした。 アトリビュート形式に変更になったことで、IDEの補完などの恩恵を受けられるようになり、かなり改善された印象です。

実際の開発現場では、このプラグインで自動生成したものをカスタマイズして利用することになると思うのでこの改善はかなり有益だと考え、OpenApi bake theme plugin でもアトリビュート形式を採用することにしました。

アトリビュート形式のサンプル

CakePHPCMS Tutorialを題材にいくつか記述例を紹介します

一覧取得API

以下が一覧取得処理をAPI化した場合の記述です。Queryは paramaters に OA\Parameterを利用して記述します

    #[OA\Get(
        path: '/api/articles.json',
        summary: 'Articles index',
        parameters: [
            new OA\Parameter(
                name: 'page',
                in: 'query',
                required: false,
                schema: new OA\Schema(type: 'number'),
                description: 'Page number to be get',
            ),
            new OA\Parameter(
                name: 'limit',
                in: 'query',
                required: false,
                schema: new OA\Schema(type: 'number'),
                description: 'Number of elements per page',
            ),
        ],
        responses: [
            new OA\Response(
                response: 201,
                description: 'OK',
                content: new OA\JsonContent(ref: '#components/schemas/Article'),
            ),
            new OA\Response(response: 401, description: 'Unauthorized'),
            new OA\Response(response: 403, description: 'Forbidden'),
        ]
    )]
    public function index()

新規作成API

新規作成APIは、JSON形式のデータをbodyに入れる形になるので、 OA\RequestBody / OA\JsonContent を使用して記述しています

    #[OA\Post(
        path: '/api/articles/add.json',
        summary: 'Add article',
        description: 'Add article',
        requestBody: new OA\RequestBody(
            required: true,
            content: new OA\JsonContent(
                required: ['user_id', 'title', 'slug', 'published', 'user', 'tags'],
                properties: [
                    new OA\Property(
                        property: 'user_id',
                        type: 'integer',
                        format: 'int32',
                        description: '',
                    ),
                    new OA\Property(
                        property: 'title',
                        type: 'string',
                        description: '',
                    ),
                    new OA\Property(
                        property: 'slug',
                        type: 'string',
                        description: '',
                    ),
                    new OA\Property(
                        property: 'body',
                        type: 'string',
                        description: '',
                    ),
                    new OA\Property(
                        property: 'published',
                        type: 'boolean',
                        description: '',
                    ),
                ]
            )
        ),
        responses: [
            new OA\Response(
                response: 201,
                description: 'OK',
                content: new OA\JsonContent(ref: '#components/schemas/Article'),
            ),
            new OA\Response(response: 401, description: 'Unauthorized'),
            new OA\Response(response: 403, description: 'Forbidden'),
            new OA\Response(
                response: 422,
                description: 'Validation Error',
                content: new OA\JsonContent(ref: '#components/schemas/Application'),
            ),
        ]
    )]
    public function add()

詳細取得API

OpenApi bake theme plugin では詳細取得APIはアソシエーションがあるModelの場合、アソシエーション内容も含むレスポンスのドキュメントを生成するように実装しています。 また、 /api/articles/{id}.json のようにidをpathに含む形式なので OA\Parameter の in を path に設定しています。

    #[OA\Get(
        path: "/api/articles/{id}.json",
        summary: "Get Article",
        description: "Get Article",
        parameters: [
            new OA\Parameter(
                name: 'id',
                in: 'path',
                required: true,
                schema: new OA\Schema(type: 'integer', format: 'int32'),
                description: 'Article id',
            ),
        ],
        responses: [
            new OA\Response(
                response: 200,
                description: 'OK',
                content: new OA\JsonContent(
                    type: 'object',
                    allOf: [
                        new OA\Schema(ref: '#components/schemas/Article'),
                        new OA\Schema(
                            properties: [
                                new OA\Property(
                                    property: "user",
                                    ref: "#/components/schemas/User",
                                    description: "User Entity",
                                ),
                                new OA\Property(
                                    property: "tags",
                                    type: "array",
                                    items: new OA\Items(ref: "#/components/schemas/Tag"),
                                    description: "Tag Entities",
                                ),
                            ],
                        ),
                    ],
                ),
            ),
            new OA\Response(response: 401, description: 'Unauthorized'),
            new OA\Response(response: 403, description: 'Forbidden'),
            new OA\Response(response: 404, description: 'Not Found'),
        ]
    )]
    public function view($id = null)

edit / delete はこれらの応用で記述できるので省略します。

まとめ

ということで、簡単にswagger-phpアトリビュート形式での記載方法を紹介しました。 このプラグインを使わないにしても、詳細取得APIでアソシエーションを含むレスポンス形式は、定義済みのOA\Schemaを利用して別形式のレスポンスを生成する記述は参考になるのではないかなと思います。

参考リンク