laravel-openapiライブラリを使ってみた（WIP）

:::note warn
すみません。本記事は記載途中です。内容的に完全記載前に世の中に出すべきだと判断しこの状態での提供になっています。
:::

## 概要

- laravel-openapiライブラリを使ってみたので簡単な使い方をまとめてみる。

## 余談

- 当該のライブラリのGithubと公式ドキュメントは下記にある。
    - Github
        - [https://github.com/vyuldashev/laravel-openapi](https://github.com/vyuldashev/laravel-openapi)
    - 公式ドキュメント
        - [https://vyuldashev.github.io/laravel-openapi/](https://vyuldashev.github.io/laravel-openapi/)
- 公式ドキュメントの記載が若干足りていなかったり誤っている事がある。しっかり使いこなすにはvendor/vyuldashevディレクトリ直下を自力で読みに行ったほうがいいかも。
- とはいえルーティング、コントローラーの記載からOpenAPIで記載されたJSONファイルを自動生成してくれるのでかなり助かる。製作者様には敬意を払い使わせてもらおうと思う。

## 導入方法

1. 導入方法と簡単な体験方法は下記にまとめた。
    - [laravel-openapiを使ってみる 本当に触りだけ](https://qiita.com/miriwo/items/93c8842f235582d7e838)

## 前提

1. laravel9のアプリケーション環境を作成する。（筆者はDockerを使用）
1. 下記コマンドを実行してUserControllerを作成する。

    ```terminal
    $ php artisan make:controller UserController
    ```

1. 作成されたコントローラーを下記のように記載する。（ライブラリの使い方を見たいだけなので実際のDBへのデータi/oの処理は割愛する。）

    ```app/Http/Controllers/UserController.php
    <?php
    
    namespace App\Http\Controllers;
    
    use Illuminate\Http\Request;
    
    class UserController extends Controller
    {
        /**
         * ユーザー作成
         *
         * @param Request $request
         */
        public function create(Request $request)
        {
            return 'create OK';
        }
    
        /**
         * ユーザー詳細
         *
         * @param integer $user_id
         */
        public function detail(int $user_id)
        {
            return 'detail OK';
        }
    }
    ```

- ルーティング（routes/api.php）に下記のような記載を行う。

    ```routes/api.php
    use App\Http\Controllers\UserController;    

    Route::post('/user', [UserController::class, 'create']);
    Route::get('/user/{user_id}', [UserController::class, 'detail']);
    ```

- リクエストとレスポンスの情報を下記に記載する。
- ユーザー作成
    - 概要

        | 項目 | 内容 | 備考 |
        | --- | --- | --- |
        | HTTPメソッド | POST |  |
        | URL | /api/user |  |
        | リクエストボディー | JSON |  |
        | レスポンス | JSON |  |

    - リクエストボディー
        - 概要

            | キー名 | 内容 | 必須 | 備考 |
            | --- | --- | --- | --- |
            | name | ユーザー名 | ○ | string |
            | email | メールアドレス | ○ | string |
            | company | 会社情報 | ○ | object |
            | +name | 会社名 | ○ | string |
            | +tel | 会社電話番号 | ○ | integer |

        - 例

            ```json
            {
              "name": "ユーザー名",
              "email": "test@example.com",
              "company": {
                "name": "会社名",
                "tel": 00000000000
              }
            }
            ```

    - レスポンス
        - 200: OK
            - 概要

                | キー名 | 内容 | 必須 | 備考 |
                | --- | --- | --- | --- |
                | id | ユーザーID | ○ | integer |
                | name | ユーザー名 | ○ | string |
                | email | メールアドレス | ○ | string |
                | company | 会社情報 | ○ | object |
                | +id | 会社ID | ○ | integer |
                | +name | 会社名 | ○ | string |
                | +tel | 会社電話番号 | ○ | integer |

            - 例

                ```json
                {
                  "id": 1
                  "name": "ユーザー名",
                  "email": "test@example.com",
                  "company": {
                    "id": 1,
                    "name": "会社名",
                    "tel": 00000000000
                  }
                }
                ```

        - 400: NG
            - 概要

                | キー名 | 内容 | 必須 | 備考 |
                | --- | --- | --- | --- |
                | message | エラーメッセージ | ○ | string |

            - 例

                ```json
                {
                  "message": "送信内容に誤りがあります。"
                }
                ```

- ユーザー情報取得
    - 概要

        | 項目 | 内容 | 備考 |
        | --- | --- | --- |
        | HTTPメソッド | GET |  |
        | URL | /api/user/{user_id} |  |
        | リクエストボディー | - | なし |
        | レスポンス | JSON |  |

    - パスパラメーター
        - 概要

            | キー名 | 内容 | 必須 | 備考 |
            | --- | --- | --- | --- |
            | user_id | ユーザーID | ○ | string |

    - レスポンス
        - 200: OK
            - 概要

                | キー名 | 内容 | 必須 | 備考 |
                | --- | --- | --- | --- |
                | id | ユーザーID | ○ | integer |
                | name | ユーザー名 | ○ | string |
                | email | メールアドレス | ○ | string |
                | company | 会社情報 | ○ | object |
                | +id | 会社ID | ○ | integer |
                | +name | 会社名 | ○ | string |
                | +tel | 会社電話番号 | ○ | integer |

            - 例

                ```json
                {
                  "id": 1
                  "name": "ユーザー名",
                  "email": "test@example.com",
                  "company": {
                    "id": 1,
                    "name": "会社名",
                    "tel": 00000000000
                  }
                }
                ```

        - 400: NG
            - 概要

                | キー名 | 内容 | 必須 | 備考 |
                | --- | --- | --- | --- |
                | message | エラーメッセージ | ○ | string |

            - 例

                ```json
                {
                  "message": "送信内容に誤りがあります。"
                }
                ```

## OpenAPIの確認方法

- 自動出力されたOpenAPIで記載されたドキュメントが正しいかどうかはSwagger Editorを用いることで確認できる。
    - オンラインでSwagger Editorを使う。
        - [https://editor.swagger.io/](https://editor.swagger.io/)
    - ローカルでSwagger Editorを使う。（セキュリティを気にするならこちら）
        - [Swagger Editorをローカルに落として開く](https://qiita.com/miriwo/items/93a845f03435fbbce3bd)

## 使用方法

### 任意のコントローラーの関数・ルーティングをOpenAPI出力の対象にする

1. まずはUserControllerクラスのcreate関数だけをOpenAPIの出力対象としてみる。
1. UserController.phpを開き下記のuse宣言を追加する。

    ```php
    use Vyuldashev\LaravelOpenApi\Attributes as OpenApi;
    ```

1. 当該クラスのクラス定義の直前に下記の内容を追記する。

    ```
    #[OpenApi\PathItem]
    ```

1. create関数の関数定義直前に下記の内容を記載する。

    ```
    #[OpenApi\Operation]
    ```

1. すべて記載すると下記のようになる。

    ```app/Http/Controllers/UserController.php
    <?php
    
    namespace App\Http\Controllers;
    
    use Illuminate\Http\Request;
    use Vyuldashev\LaravelOpenApi\Attributes as OpenApi;
    
    #[OpenApi\PathItem]
    class UserController extends Controller
    {
        /**
         * ユーザー作成
         *
         * @param Request $request
         */
        #[OpenApi\Operation]
        public function create(Request $request)
        {
            return 'create OK';
        }
    
        /**
         * ユーザー詳細
         *
         * @param integer $user_id
         */
        public function detail(int $user_id)
        {
            return 'detail OK';
        }
    }
    ```

1. この状態で下記コマンドを実行してOpenAPIドキュメントを出力してみる。（`default`は`config/openapi.php`に記載されている出力する際の設定（コレクション）を指定している。指定しなかった場合default設定が適用されるが今回、説明のために敢えて記載している。）

    ```
    $ php artisan openapi:generate default
    ```

1. 下記のような内容が出力された。こちらのライブラリはJSONでのみOpenAPIファイルが出力される。（ファイルに出力したい場合は前述のコマンドの内容を`>`などでファイルにリダイレクトすれば良い。）

    ```openapi.json
    {
        "openapi": "3.0.2",
        "info": {
            "title": "Laravel",
            "version": "1.0.0"
        },
        "servers": [
            {
                "url": "http:\/\/localhost"
            }
        ],
        "paths": {
            "\/api\/user": {
                "post": {
                    "summary": "ユーザー作成"
                }
            }
        }
    }
    ```

1. `#[OpenApi\Operation]`を記載したcreate関数のみOpenAPIで記載されたドキュメントに情報がある事が分かる。
1. Swagger Editorで確認するとpathsのルートオブジェクトに必須の要素が足りていないためエラーが出る。しかし致命的なエラー（描画できないなど）は出ていないようだ。

    ![Swagger_Editor.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/306417/baccf15f-f4d3-781b-56f6-9f8d0b73be52.png)

1. GETとPOST両方の関数がOpenAPIドキュメントの対象となるように下記の様にコントローラーを修正した。

    ```app/Http/Controllers/UserController.php
    <?php
    
    namespace App\Http\Controllers;
    
    use Illuminate\Http\Request;
    use Vyuldashev\LaravelOpenApi\Attributes as OpenApi;
    
    #[OpenApi\PathItem]
    class UserController extends Controller
    {
        /**
         * ユーザー作成
         *
         * @param Request $request
         */
        #[OpenApi\Operation]
        public function create(Request $request)
        {
            return 'create OK';
        }
    
        /**
         * ユーザー詳細
         *
         * @param integer $user_id
         */
        #[OpenApi\Operation]
        public function detail(int $user_id)
        {
            return 'detail OK';
        }
    }
    ```

### リクエストボディーの設定

1. リクエストボディーの設定方法をまとめる。
1. 下記の手順で設定する。
    1. スキーマクラスにてオブジェクトを定義
    1. リクエストボディークラスにてスキーマクラスを呼び出す。
1. POSTのAPIのリクエストボディーを設定するために下記のように作業する。
    1. CreateUserRequestBodySchemaクラスにてキーと値の型を定義
    1. CreateUserRequestBodyクラスにてCreateUserRequestBodySchemaクラスの呼び出し
1. CreateUserRequestBodySchemaクラスの作成
    1. 下記コマンドを実行してCreateUserRequestBodyクラスを作成する。

        ```terminal
        $ php artisan openapi:make-schema CreateUserRequestBodySchema
        ```
    1. CreateUserRequestBodySchema.phpを下記のように記載する。

        ```app/OpenApi/Schemas/CreateUserRequestBodySchema.php
        <?php
        
        namespace App\OpenApi\Schemas;
        
        use GoldSpecDigital\ObjectOrientedOAS\Contracts\SchemaContract;
        use GoldSpecDigital\ObjectOrientedOAS\Objects\AllOf;
        use GoldSpecDigital\ObjectOrientedOAS\Objects\AnyOf;
        use GoldSpecDigital\ObjectOrientedOAS\Objects\Not;
        use GoldSpecDigital\ObjectOrientedOAS\Objects\OneOf;
        use GoldSpecDigital\ObjectOrientedOAS\Objects\Schema;
        use Vyuldashev\LaravelOpenApi\Contracts\Reusable;
        use Vyuldashev\LaravelOpenApi\Factories\SchemaFactory;
        
        class CreateUserRequestBodySchema extends SchemaFactory implements Reusable
        {
            /**
             * @return AllOf|OneOf|AnyOf|Not|Schema
             */
            public function build(): SchemaContract
            {
                return Schema::object('UserRequestBody')
                    ->properties(
                        Schema::string('name'),
                        Schema::string('email'),
                        Schema::object('company')
                            ->properties(
                                Schema::string('name'),
                                Schema::integer('tel'),
                            ),
                    );
            }
        }
        ```
1. CreateUserRequestBodyクラスの作成
    1. 下記コマンドを実行してCreateUserRequestBodyクラスを作成する。

        ```terminal
        $ php artisan openapi:make-requestBody CreateUserRequestBody
        ```

    1. `app/OpenApi/RequestBodies/`直下にCreateUserRequestBody.phpが作成されるので開く。
    1. CreateUserRequestBody.phpを下記のように記載する。

        ```app/OpenApi/RequestBodies/CreateUserRequestBody.php
        <?php
        
        namespace App\OpenApi\RequestBodies;
        
        use App\OpenApi\Schemas\CreateUserRequestBodySchema;
        use GoldSpecDigital\ObjectOrientedOAS\Objects\MediaType;
        use GoldSpecDigital\ObjectOrientedOAS\Objects\RequestBody;
        use Vyuldashev\LaravelOpenApi\Factories\RequestBodyFactory;
        
        class CreateUserRequestBody extends RequestBodyFactory
        {
            public function build(): RequestBody
            {
                return RequestBody::create('CreateUser')
                    ->description('登録するユーザーの情報')
                    ->required(true)
                    ->content(
                        MediaType::json()->schema(CreateUserRequestBodySchema::ref())
                    );
            }
        }
        ```

    1. 次に既に定義したCreateUserRequestBody.phpをコントローラーのアノテーションで設定する。

        ```app/Http/Controllers/UserController.php
        <?php
        
        namespace App\Http\Controllers;
        
        use App\OpenApi\RequestBodies\CreateUserRequestBody;
        use Illuminate\Http\Request;
        use Vyuldashev\LaravelOpenApi\Attributes as OpenApi;
        
        #[OpenApi\PathItem]
        class UserController extends Controller
        {
            /**
             * ユーザー作成
             *
             * @param Request $request
             */
            #[OpenApi\Operation]
            #[OpenApi\RequestBody(factory: CreateUserRequestBody::class)]
            public function create(Request $request)
            {
                return 'create OK';
            }
        
            /**
             * ユーザー詳細
             *
             * @param integer $user_id
             */
            #[OpenApi\Operation]
            public function detail(int $user_id)
            {
                return 'detail OK';
            }
        }
        ```

1. GETのAPIはリクエストボディーば無いため特に記載は行わない。
1. これまでの状態で出力したOpenAPIドキュメントを下記に記載する。

    ```openapi.json
    {
        "openapi": "3.0.2",
        "info": {
            "title": "Laravel",
            "version": "1.0.0"
        },
        "servers": [
            {
                "url": "http:\/\/localhost"
            }
        ],
        "paths": {
            "\/api\/user": {
                "post": {
                    "summary": "ユーザー作成",
                    "requestBody": {
                        "description": "登録するユーザーの情報",
                        "content": {
                            "application\/json": {
                                "schema": {
                                    "$ref": "#\/components\/schemas\/UserRequestBody"
                                }
                            }
                        },
                        "required": true
                    }
                }
            },
            "\/api\/user\/{user_id}": {
                "get": {
                    "summary": "ユーザー詳細",
                    "parameters": [
                        {
                            "name": "user_id",
                            "in": "path",
                            "description": "",
                            "required": true,
                            "schema": {
                                "type": "integer"
                            }
                        }
                    ]
                }
            }
        },
        "components": {
            "schemas": {
                "UserRequestBody": {
                    "type": "object",
                    "properties": {
                        "name": {
                            "type": "string"
                        },
                        "email": {
                            "type": "string"
                        },
                        "company": {
                            "type": "object",
                            "properties": {
                                "name": {
                                    "type": "string"
                                },
                                "tel": {
                                    "type": "integer"
                                }
                            }
                        }
                    }
                }
            }
        }
    }
    ```

### レスポンスの設定

1. レスポンスの設定方法をまとめる。
1. 下記の手順で設定する。
    1. スキーマクラスにてオブジェクトを定義
    1. レスポンスクラスにてスキーマクラスを呼び出す。
1. 今回はPOSTのAPIもGETのAPIも同じ値が返るので下記の作業一回でレスポンスの定義は完了する。
    1. OkUserResponseSchemaクラスにてキーと値の型を定義
    1. OkCreateUserResponseクラスにてOkUserResponseSchemaクラスの呼び出し
    1. OkUserResponseクラスにてOkUserResponseSchemaクラスの呼び出し
    1. コントローラーで設定
    1. BadRequestResponseSchemaクラスにてキーと値の型を定義
    1. BadRequestResponseクラスにてBadRequestResponseSchemaクラスの呼び出し
    1. コントローラーで設定
1. OkUserResponseSchemaクラスの作成
    1. 下記コマンドを実行してOkUserResponseSchema.phpを作成する。

        ```terminal
        $ php artisan openapi:make-schema OkUserResponseSchema
        ```

    1. 下記の様に記載する。

        ```app/OpenApi/Schemas/OkUserResponseSchema.php
        <?php
        
        namespace App\OpenApi\Schemas;
        
        use GoldSpecDigital\ObjectOrientedOAS\Contracts\SchemaContract;
        use GoldSpecDigital\ObjectOrientedOAS\Objects\AllOf;
        use GoldSpecDigital\ObjectOrientedOAS\Objects\AnyOf;
        use GoldSpecDigital\ObjectOrientedOAS\Objects\Not;
        use GoldSpecDigital\ObjectOrientedOAS\Objects\OneOf;
        use GoldSpecDigital\ObjectOrientedOAS\Objects\Schema;
        use Vyuldashev\LaravelOpenApi\Contracts\Reusable;
        use Vyuldashev\LaravelOpenApi\Factories\SchemaFactory;
        
        class OkUserResponseSchema extends SchemaFactory implements Reusable
        {
            /**
             * @return AllOf|OneOf|AnyOf|Not|Schema
             */
            public function build(): SchemaContract
            {
                return Schema::object('OkUserResponse')
                    ->properties(
                        Schema::integer('id'),
                        Schema::string('name'),
                        Schema::string('email'),
                        Schema::object('company')
                            ->properties(
                                Schema::integer('id'),
                                Schema::string('name'),
                                Schema::integer('tel'),
                            ),
                    );
            }
        }
        ```

1. OkCreateUserResponseクラスの作成
    1. 下記コマンドを実行してOkCreateUserResponse.phpを作成する。

        ```terminal
        $ php artisan openapi:make-response OkCreateUserResponse
        ```

    1. 下記のように記載する。

        ```app/OpenApi/Responies/OkCreateUserResponse.php
        <?php
        
        namespace App\OpenApi\Responses;
        
        use App\OpenApi\Schemas\OkUserResponseSchema;
        use GoldSpecDigital\ObjectOrientedOAS\Objects\MediaType;
        use GoldSpecDigital\ObjectOrientedOAS\Objects\Response;
        use Vyuldashev\LaravelOpenApi\Factories\ResponseFactory;
        
        class OkCreateUserResponse extends ResponseFactory
        {
            public function build(): Response
            {
                return Response::ok()
                    ->description('成功レスポンス')
                    ->content(
                        MediaType::json()
                            ->schema(OkUserResponseSchema::ref())
                    );
            }
        
        }
        ```

1. OkUserResponseクラスの作成
    1. 下記コマンドを実行してOkUserResponse.phpを作成する。

        ```terminal
        $ php artisan openapi:make-response OkUserResponse
        ```

    1. 下記のように記載する　。

        ```app/OpenApi/Responsies/OkUserResponse.php
        <?php
        
        namespace App\OpenApi\Responses;
        
        use App\OpenApi\Schemas\OkUserResponseSchema;
        use GoldSpecDigital\ObjectOrientedOAS\Objects\MediaType;
        use GoldSpecDigital\ObjectOrientedOAS\Objects\Response;
        use Vyuldashev\LaravelOpenApi\Factories\ResponseFactory;
        
        class OkUserResponse extends ResponseFactory
        {
            public function build(): Response
            {
                return Response::ok()
                    ->description('成功レスポンス')
                    ->content(
                        MediaType::json()
                            ->schema(OkUserResponseSchema::ref())
                    );
            }
        }
        ```

1. ここまで記載した内容をコントローラーの関数にアノテーションで設定する。
1. 下記のようにコントローラーを記載する。

    ```app/Http/Controllers/UserController.php
    <?php
    
    namespace App\Http\Controllers;
    
    use App\OpenApi\RequestBodies\CreateUserRequestBody;
    use App\OpenApi\Responses\OkCreateUserResponse;
    use App\OpenApi\Responses\OkUserResponse;
    use Illuminate\Http\Request;
    use Vyuldashev\LaravelOpenApi\Attributes as OpenApi;
    
    #[OpenApi\PathItem]
    class UserController extends Controller
    {
        /**
         * ユーザー作成
         *
         * @param Request $request
         */
        #[OpenApi\Operation]
        #[OpenApi\RequestBody(factory: CreateUserRequestBody::class)]
        #[OpenApi\Response(factory: OkCreateUserResponse::class)]
        public function create(Request $request)
        {
            return 'create OK';
        }
    
        /**
         * ユーザー詳細
         *
         * @param integer $user_id
         */
        #[OpenApi\Operation]
        #[OpenApi\Response(factory: OkUserResponse::class)]
        public function detail(int $user_id)
        {
            return 'detail OK';
        }
    }
    ```

1. BadRequestResponseSchemaクラスの作成
    1. 下記コマンドを実行してBadRequestResponseSchema.phpを作成する。

        ```termianl
        $ php artisan openapi:make-schema BadRequestResponseSchema
        ```

    1. 下記のように記載する。

        ```app/OpenApi/Schemas/BadRequestResponseSchema.php
        <?php
        
        namespace App\OpenApi\Schemas;
        
        use GoldSpecDigital\ObjectOrientedOAS\Contracts\SchemaContract;
        use GoldSpecDigital\ObjectOrientedOAS\Objects\AllOf;
        use GoldSpecDigital\ObjectOrientedOAS\Objects\AnyOf;
        use GoldSpecDigital\ObjectOrientedOAS\Objects\Not;
        use GoldSpecDigital\ObjectOrientedOAS\Objects\OneOf;
        use GoldSpecDigital\ObjectOrientedOAS\Objects\Schema;
        use Vyuldashev\LaravelOpenApi\Contracts\Reusable;
        use Vyuldashev\LaravelOpenApi\Factories\SchemaFactory;
        
        class BadRequestResponseSchema extends SchemaFactory implements Reusable
        {
            /**
             * @return AllOf|OneOf|AnyOf|Not|Schema
             */
            public function build(): SchemaContract
            {
                return Schema::object('BadRequestResponse')
                    ->properties(
                        Schema::string('message')
                    );
            }
        }
        ```

1. BadRequestResponseクラスの作成
    1. 下記コマンドを実行してBadRequestResponse.phpを作成する。

        ```terminal
        $ php artisan openapi:make-response BadRequestResponse
        ```

    1. 下記のように記載する。

        ```app/OpenApi/Responses/BadRequestResponse.php
        <?php
        
        namespace App\OpenApi\Responses;
        
        use App\OpenApi\Schemas\BadRequestResponseSchema;
        use GoldSpecDigital\ObjectOrientedOAS\Objects\MediaType;
        use GoldSpecDigital\ObjectOrientedOAS\Objects\Response;
        use Vyuldashev\LaravelOpenApi\Factories\ResponseFactory;
        
        class BadRequestResponse extends ResponseFactory
        {
            public function build(): Response
            {
                return Response::badRequest()
                    ->description('Bad Request')
                    ->content(
                        MediaType::json()
                            ->schema(BadRequestResponseSchema::ref())
                    );
            }
        }
        ```

1. ここまで記載した内容をコントローラーの関数にアノテーションで設定する。
1. 下記のようにコントローラーを記載する。

    ```app/Http/Controllers/UserController.php
    <?php
    
    namespace App\Http\Controllers;
    
    use App\OpenApi\RequestBodies\CreateUserRequestBody;
    use App\OpenApi\Responses\BadRequestResponse;
    use App\OpenApi\Responses\OkCreateUserResponse;
    use App\OpenApi\Responses\OkUserResponse;
    use Illuminate\Http\Request;
    use Vyuldashev\LaravelOpenApi\Attributes as OpenApi;
    
    #[OpenApi\PathItem]
    class UserController extends Controller
    {
        /**
         * ユーザー作成
         *
         * @param Request $request
         */
        #[OpenApi\Operation]
        #[OpenApi\RequestBody(factory: CreateUserRequestBody::class)]
        #[OpenApi\Response(factory: OkCreateUserResponse::class)]
        #[OpenApi\Response(factory: BadRequestResponse::class)]
        public function create(Request $request)
        {
            return 'create OK';
        }
    
        /**
         * ユーザー詳細
         *
         * @param integer $user_id
         */
        #[OpenApi\Operation]
        #[OpenApi\Response(factory: OkUserResponse::class)]
        #[OpenApi\Response(factory: BadRequestResponse::class)]
        public function detail(int $user_id)
        {
            return 'detail OK';
        }
    }
    ```

1. ここまで記載できたら下記のコマンドを実行してOpenAPIで記載されたドキュメントを出力してみる。

    ```terminal
    $ php artisan openapi:generate default
    ```

1. 下記のようにターミナルに出力された。

    ```openapi.json
    {
        "openapi": "3.0.2",
        "info": {
            "title": "Laravel",
            "version": "1.0.0"
        },
        "servers": [
            {
                "url": "http:\/\/localhost"
            }
        ],
        "paths": {
            "\/api\/user": {
                "post": {
                    "summary": "ユーザー作成",
                    "requestBody": {
                        "description": "登録するユーザーの情報",
                        "content": {
                            "application\/json": {
                                "schema": {
                                    "$ref": "#\/components\/schemas\/UserRequestBody"
                                }
                            }
                        },
                        "required": true
                    },
                    "responses": {
                        "200": {
                            "description": "成功レスポンス",
                            "content": {
                                "application\/json": {
                                    "schema": {
                                        "$ref": "#\/components\/schemas\/OkUserResponse"
                                    }
                                }
                            }
                        },
                        "400": {
                            "description": "Bad Request",
                            "content": {
                                "application\/json": {
                                    "schema": {
                                        "$ref": "#\/components\/schemas\/BadRequestResponse"
                                    }
                                }
                            }
                        }
                    }
                }
            },
            "\/api\/user\/{user_id}": {
                "get": {
                    "summary": "ユーザー詳細",
                    "parameters": [
                        {
                            "name": "user_id",
                            "in": "path",
                            "description": "",
                            "required": true,
                            "schema": {
                                "type": "integer"
                            }
                        }
                    ],
                    "responses": {
                        "200": {
                            "description": "成功レスポンス",
                            "content": {
                                "application\/json": {
                                    "schema": {
                                        "$ref": "#\/components\/schemas\/OkUserResponse"
                                    }
                                }
                            }
                        },
                        "400": {
                            "description": "Bad Request",
                            "content": {
                                "application\/json": {
                                    "schema": {
                                        "$ref": "#\/components\/schemas\/BadRequestResponse"
                                    }
                                }
                            }
                        }
                    }
                }
            }
        },
        "components": {
            "schemas": {
                "BadRequestResponse": {
                    "type": "object",
                    "properties": {
                        "message": {
                            "type": "string"
                        }
                    }
                },
                "UserRequestBody": {
                    "type": "object",
                    "properties": {
                        "name": {
                            "type": "string"
                        },
                        "email": {
                            "type": "string"
                        },
                        "company": {
                            "type": "object",
                            "properties": {
                                "name": {
                                    "type": "string"
                                },
                                "tel": {
                                    "type": "integer"
                                }
                            }
                        }
                    }
                },
                "OkUserResponse": {
                    "type": "object",
                    "properties": {
                        "id": {
                            "type": "integer"
                        },
                        "name": {
                            "type": "string"
                        },
                        "email": {
                            "type": "string"
                        },
                        "company": {
                            "type": "object",
                            "properties": {
                                "id": {
                                    "type": "integer"
                                },
                                "name": {
                                    "type": "string"
                                },
                                "tel": {
                                    "type": "integer"
                                }
                            }
                        }
                    }
                }
            }
        }
    }
    ```

- ちなみにどの関数を呼び出せばどのHTTPステータスコードのレスポンス定義ができるかは下記クラスの関数の中を見れば分かる。
    - `vendor/goldspecdigital/oooas/src/Objects/Response.php`

### クエリパラメーターの設定

1. laravel-openapiを用いているとパスパラメーターはルーティング情報から勝手に拾ってドキュメント化してくれる。
1. しかし、クエリパラメーターは自分で定義する必要があるため次に記載する。
1. 今回のPOSTとGETのAPIにはクエリパラメーターは存在しないが説明のため設定方法を記載する。
1. クエリパラメータークラスの作成
    1. 下記コマンドを実行してQueryParameter.phpを作成する。

        ```terminal
        $ php artisan openapi:make-parameters Query
        ```

    1. 下記のように記載する。

        ```app/OpenApi/Parameters/QueryParameters.php
        <?php
        
        namespace App\OpenApi\Parameters;
        
        use GoldSpecDigital\ObjectOrientedOAS\Objects\Parameter;
        use GoldSpecDigital\ObjectOrientedOAS\Objects\Schema;
        use Vyuldashev\LaravelOpenApi\Factories\ParametersFactory;
        
        class QueryParameters extends ParametersFactory
        {
            /**
             * @return Parameter[]
             */
            public function build(): array
            {
                return [
                
                    Parameter::query()
                        ->name('user_id')
                        ->description('ユーザーID')
                        ->required(false)
                        ->schema(Schema::integer()),
                
                ];
            }
        }
        ```

1. コントローラーに定義したパラメーターを設定する（今回はGETのAPIに設定）

    ```app/Http/Controllers/UserController.php
    <?php
    
    namespace App\Http\Controllers;
    
    use App\OpenApi\Parameters\QueryParameters;
    use App\OpenApi\RequestBodies\CreateUserRequestBody;
    use App\OpenApi\Responses\BadRequestResponse;
    use App\OpenApi\Responses\OkCreateUserResponse;
    use App\OpenApi\Responses\OkUserResponse;
    use Illuminate\Http\Request;
    use Vyuldashev\LaravelOpenApi\Attributes as OpenApi;
    
    #[OpenApi\PathItem]
    class UserController extends Controller
    {
        /**
         * ユーザー作成
         *
         * @param Request $request
         */
        #[OpenApi\Operation]
        #[OpenApi\RequestBody(factory: CreateUserRequestBody::class)]
        #[OpenApi\Response(factory: OkCreateUserResponse::class)]
        #[OpenApi\Response(factory: BadRequestResponse::class)]
        public function create(Request $request)
        {
            return 'create OK';
        }
    
        /**
         * ユーザー詳細
         *
         * @param integer $user_id
         */
        #[OpenApi\Operation]
        #[OpenApi\Response(factory: OkUserResponse::class)]
        #[OpenApi\Response(factory: BadRequestResponse::class)]
        #[OpenApi\Parameters(factory: QueryParameters::class)]
        public function detail(int $user_id)
        {
            return 'detail OK';
        }
    }
    ```

- OpenAPIを出力すると下記の様になる。

    ```openapi.json
    {
        "openapi": "3.0.2",
        "info": {
            "title": "Laravel",
            "version": "1.0.0"
        },
        "servers": [
            {
                "url": "http:\/\/localhost"
            }
        ],
        "paths": {
            "\/api\/user": {
                "post": {
                    "summary": "ユーザー作成",
                    "requestBody": {
                        "description": "登録するユーザーの情報",
                        "content": {
                            "application\/json": {
                                "schema": {
                                    "$ref": "#\/components\/schemas\/UserRequestBody"
                                }
                            }
                        },
                        "required": true
                    },
                    "responses": {
                        "200": {
                            "description": "成功レスポンス",
                            "content": {
                                "application\/json": {
                                    "schema": {
                                        "$ref": "#\/components\/schemas\/OkUserResponse"
                                    }
                                }
                            }
                        },
                        "400": {
                            "description": "Bad Request",
                            "content": {
                                "application\/json": {
                                    "schema": {
                                        "$ref": "#\/components\/schemas\/BadRequestResponse"
                                    }
                                }
                            }
                        }
                    }
                }
            },
            "\/api\/user\/{user_id}": {
                "get": {
                    "summary": "ユーザー詳細",
                    "parameters": [
                        {
                            "name": "user_id",
                            "in": "path",
                            "description": "",
                            "required": true,
                            "schema": {
                                "type": "integer"
                            }
                        },
                        {
                            "name": "user_id",
                            "in": "query",
                            "description": "ユーザーID",
                            "required": false,
                            "schema": {
                                "type": "integer"
                            }
                        }
                    ],
                    "responses": {
                        "200": {
                            "description": "成功レスポンス",
                            "content": {
                                "application\/json": {
                                    "schema": {
                                        "$ref": "#\/components\/schemas\/OkUserResponse"
                                    }
                                }
                            }
                        },
                        "400": {
                            "description": "Bad Request",
                            "content": {
                                "application\/json": {
                                    "schema": {
                                        "$ref": "#\/components\/schemas\/BadRequestResponse"
                                    }
                                }
                            }
                        }
                    }
                }
            }
        },
        "components": {
            "schemas": {
                "BadRequestResponse": {
                    "type": "object",
                    "properties": {
                        "message": {
                            "type": "string"
                        }
                    }
                },
                "UserRequestBody": {
                    "type": "object",
                    "properties": {
                        "name": {
                            "type": "string"
                        },
                        "email": {
                            "type": "string"
                        },
                        "company": {
                            "type": "object",
                            "properties": {
                                "name": {
                                    "type": "string"
                                },
                                "tel": {
                                    "type": "integer"
                                }
                            }
                        }
                    }
                },
                "OkUserResponse": {
                    "type": "object",
                    "properties": {
                        "id": {
                            "type": "integer"
                        },
                        "name": {
                            "type": "string"
                        },
                        "email": {
                            "type": "string"
                        },
                        "company": {
                            "type": "object",
                            "properties": {
                                "id": {
                                    "type": "integer"
                                },
                                "name": {
                                    "type": "string"
                                },
                                "tel": {
                                    "type": "integer"
                                }
                            }
                        }
                    }
                }
            }
        }
    }
    ```

- Parametersにqueryが追加された。

    ![Swagger_Editor.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/306417/3b904a4b-5500-644c-0a99-b3d25fe57410.png)

### 例とコメントの設定（WIP）

1. リクエストやレスポンスに例として値を設定する方法をまとめる。
1. laravel-openapiはスキーマクラスて定義した型から勝手に例を設定してくれる。
1. しかし例の値を任意に設定することもできる。
1. 既に定義されているスキーマクラスの各値にメソッドチェーンでexampleを定義できる。
1. また、説明もメソットチェーンで定義できる。
1. CreateUserRequestBodySchema.phpのdescriptionとexampleの記載
    1. 下記のように記載する。
1. OkUserResponseSchema.phpのdescriptionとexampleの記載
1. BadRequestResponseSchemaのdescriptionとexampleの記載

### タグ定義と設定（WIP）

### コレクションの設定（WIP）

### セキュリティーの設定（WIP）

## 参考文献

- [https://vyuldashev.github.io/laravel-openapi/](https://vyuldashev.github.io/laravel-openapi/)
- [https://github.com/vyuldashev/laravel-openapi](https://github.com/vyuldashev/laravel-openapi)