arkitechdev/openapi

用于创建 OpenAPI 3 规范文件的包。

v0.9.2 2021-09-24 06:24 UTC

This package is auto-updated.

Last update: 2024-09-24 21:41:18 UTC


README

需求

  • PHP 7.3 | PHP 8.0
  • Laravel 8(如果您想使用外观和命令的话)

安装

使用 composer 安装

composer require arkitechdev/openapi

您可以通过运行以下 artisan 命令生成文档:

php artisan openapi:generate

该命令会为您创建配置文件,但您也可以通过以下命令发布它:

php artisan vendor:publish

配置

配置文件看起来像这样

<?php

return [

    /**
     * The default path to the file containing the docs.
     */
    'docs_path' => base_path('docs.php'),

    /**
     * The path to the file where the generated documentation will be stored.
     * The generator supports both .json and .yaml files.
     */
    'output_path' => base_path('docs.json'),

];

docs_path 包含您的文档规范文件的路径。

output_path 是您希望存储生成的 API 文档的位置。如果文件名有 .json 扩展名,它将创建为一个 JSON 文件。如果文件名有 .yaml 扩展名,它将创建为一个 YAML 文件。很神奇。

入门

该库期望在项目的根目录中有一个名为 docs.php 的文件。您可以在配置文件中自定义此文件。要开始编写文档,您可以使用 OpenApi 外观。它的工作方式与 Laravel 中创建路由非常相似。以下是一个示例

docs.php

<?php

use Arkitechdev\OpenApi\Facades\OpenApi;

OpenApi::title('Sample API');
OpenApi::description('This is just an example of a docs file');
OpenApi::version('1.0.0');

OpenApi::addServer('/', 'localhost');

OpenApi::schemas([
    Schemas\Post::class,
    Schemas\User::class,
    Schemas\Image::class,
]);

OpenApi::get('/posts', \App\Docs\Requests\PostIndex::class);
OpenApi::post('/posts', \App\Docs\Requests\PostStore::class);
OpenApi::get('/posts/{id}', \App\Docs\Requests\PostShow::class);
OpenApi::patch('/posts/{id}', \App\Docs\Requests\PostUpdate::class);
OpenApi::delete('/posts/{id}', \App\Docs\Requests\PostDestroy::class);

OpenApi::get('/users', \App\Docs\Requests\UserIndex::class);
OpenApi::post('/users', \App\Docs\Requests\UserStore::class);
OpenApi::get('/users/{id}', \App\Docs\Requests\UserShow::class);
OpenApi::patch('/users/{id}', \App\Docs\Requests\UserUpdate::class);
OpenApi::delete('/users/{id}', \App\Docs\Requests\UserDestroy::class);

然后,只需使用 artisan 命令生成 Open API 文档的 json 或 yaml 文件。简单!

有各种方法来构建您的文档。下面将进行解释。

方法

类中的大多数方法都相对直观,并提供自动补全功能。尽管如此,这里还有一些值得提到的事情。

获取器和设置器

许多方法要么设置属性值并返回自身,要么在没有设置或为 null 的参数的情况下返回属性值。

示例

OpenApi::title('A title')

这会将 title 属性设置为 A title 并返回 OpenApi 对象。这允许进行链式操作,例如

OpenApi::title('A title')->description('Whatever')->version('1.0.0')

非常方便。同时,如果没有设置参数,则返回值

$title = OpenApi::title() // $title = 'A title'

回调

一些方法提供了回调。当设置子对象(如参数或属性)时,该方法提供了一个可选的回调,用于进一步定义对象。根据您添加的类型,回调的第一个和唯一参数是添加的对象。

考虑以下示例

Request::method('get')->addParameter('page', null, function (Parameter $parameter) {
    $parameter->in(Parameter::IN_QUERY)->type(Parameter::TYPE_INTEGER)->required(true);
});

由于我们添加了一个参数,所以回调的参数是一个 Parameter 对象。多么好的例子。在这里,我们添加了一个名为 'page' 的 query 参数,类型为 integer 并将其设置为 required

您可以用这种方式构建整个 API 文档,尽管我不建议这样做。请考虑以下相当冗长的示例。

OpenApi::title('API example')
    ->version('1.0.0')
    ->addPath('patch', '/posts/{id}', null, function (Request $request) {
        $request->tags([
                'Posts'
            ])
            ->addParameter('id', null, function (Parameter $parameter) {
                $parameter->in(Parameter::IN_PATH)
                    ->type(Parameter::TYPE_INTEGER)
                    ->required(true);
            })
            ->addContentType(Request::TYPE_JSON, null, function (Schema $schema) {
                $schema->type(Schema::TYPE_OBJECT)
                    ->addProperty('title', null, function (Property $property) {
                        $property->type(Property::TYPE_STRING)
                            ->required(true);
                    })
                    ->addProperty('subject', null, function (Property $property) {
                        $property->type(Property::TYPE_STRING)
                            ->format(Property::FORMAT_DATETIME);
                    })
                    ->addProperty('content', null, function (Property $property) {
                        $property->type(Property::TYPE_STRING);
                    });
            })
            ->addResponse(200, function (Response $response) {
                $response->addContentType(Response::TYPE_JSON, null, function (Schema $schema) {
                    $schema->type(Schema::TYPE_OBJECT)
                        ->addProperty('title', null, function (Property $property) {
                            $property->type(Property::TYPE_STRING);
                        })
                        ->addProperty('subject', null, function (Property $property) {
                            $property->type(Property::TYPE_STRING)
                                ->format(Property::FORMAT_DATETIME);
                        })
                        ->addProperty('content', null, function (Property $property) {
                            $property->type(Property::TYPE_STRING);
                        });
                });
            });
    });

它很快就会变得难以管理,这就是为什么这个库支持将一切拆分为类。我将在下一章中介绍这一点。

方法默认值

如果您只想创建参数和属性而不需要所有这些杂七杂八的东西,那么我有您的后背。注意上面示例中的那些 null 参数?那就是类型所在的地方。

示例

$request->addParameter('title', Parameter::TYPE_STRING);
$request->addParameter('page', Parameter::TYPE_INTEGER);
// or if you hate constants for some reason:
$request->addParameter('title', 'string');
$request->addParameter('page', 'integer');

实际上,string 是参数和属性的默认类型,所以对于该类型可以进一步简化

$request->addParameter('title');
$schema->addProperty('content');

您已经明白了。

其他类型有不同的默认值。请自由地深入研究代码。

基于类的 API 文档

现在,上面的大例子只是一个单个端点。想象一下有100个端点。这将使维护变得更加困难。没有人有那么多时间去处理这些!

因此,如果add...()方法的第一个参数是一个类,那么你可以将里面的所有内容都隐藏起来。语法看起来是这样的

$property->addProperty(Property::class);
$request->addContentType(Schema::class);
$request->addParameter(Parameter::class);
$request->addResponse(Response::class);
$response->addContentType(Schema::class);
$schema->addProperty(Property::class);

只要它扩展了上面列表中的相关类,你就可以创建和使用任何你想要的类。然后,你可以在该类内部使用类属性和继承的方法来定义它。

请求以这种方式工作得很好。在你的文档文件中,你可以这样做

docs.php

OpenApi::get('/projects', ProjectIndex::class);

自定义类可能看起来像这样

ProjectIndex.php

<?php

namespace App\Requests;

use App\Schemas\PaginationLinks as PaginationLinksSchema;
use App\Schemas\PaginationMeta as PaginationMetaSchema;
use App\Schemas\Project as ProjectSchema;
use Arkitechdev\OpenApi\Parameter;
use Arkitechdev\OpenApi\Property;
use Arkitechdev\OpenApi\Request;
use Arkitechdev\OpenApi\Response;
use Arkitechdev\OpenApi\Schema;

class ProjectIndex extends Request
{
    protected string $method = 'get';

    protected string $description = 'The description goes here';

    protected string $summary = 'The summary goes here';

    protected array $tags = [
        'Projects'
    ];

    public function __construct()
    {
        $this->addParameter('searchQuery', null, function (Parameter $parameter) {
            $parameter->in(Parameter::IN_QUERY)->required(false);
        });

        $this->addParameter('page', Parameter::TYPE_INTEGER, function (Parameter $parameter) {
            $parameter->in(Parameter::IN_QUERY)->required(false)->example(1)->default(1);
        });

        $this->addParameter('per_page', Parameter::TYPE_INTEGER, function (Parameter $parameter) {
            $parameter->in(Parameter::IN_QUERY)->required(false)->example(15)->default(15);
        });

        $this->addResponse(200, function (Response $response) {
            $response->description('Returns the list of projects')
                ->addContentType(Response::TYPE_JSON, null, function (Schema $schema) {
                    $schema->addProperty('data', Property::TYPE_ARRAY, function (Property $property) {
                        $property->ref(ProjectSchema::class);
                    })->addProperty('links', null, function (Property $property) {
                        $property->ref(PaginationLinksSchema::class);
                    })->addProperty('meta', null, function (Property $property) {
                        $property->ref(PaginationMetaSchema::class);
                    });
                });
        });
    }
}

直接来自Laraville。在这个例子中,我们没有创建Parameter、Response或Property类,但你完全可以做得更多。你可以根据需要将其深入到任意层级。

尽管如此,我们确实创建了Schema类。这样我们就可以将它们用作参考(稍后会有更多介绍)。

我知道你很想知道,所以这就是Project架构

Project.php

<?php

namespace App\Schemas;

use Arkitechdev\OpenApi\Property;
use Arkitechdev\OpenApi\Schema;

class Project extends Schema
{
    protected string $type = 'object';

    public function __construct()
    {
        $this->addProperty('id', Property::TYPE_INTEGER, function (Property $property) {
            $property->example(1)->required(true);
        });

        $this->addProperty('name', null, function (Property $property) {
            $property->example('My cool project')->required(true);
        });

        $this->addProperty('description', null, function (Property $property) {
            $property->example('Nice long description')->required(true);
        });

        $this->addProperty('created', null, function (Property $property) {
            $property->format(Property::FORMAT_DATETIME);
        });
    }
}

注意:如果你需要覆盖自定义类中的某些内容,仍然可以使用回调。非常好!

参考文献

如果你计划在属性中使用架构的引用($property->ref(Project::class)),你必须确保将它们添加到OpenApi对象的架构数组中

docs.php

OpenApi::schemas([
    Project::class,
]);

验证器

有一个带有内置验证器的在线编辑器,你可以使用它。

当你生成了你的JSON/YAML文件后,你可以将其内容粘贴到编辑器中,并检查它看起来是否正常。

路线图

我想要用这个包做的事情(不分先后顺序)

  • 添加更多OpenApi功能
  • 创建特性和Laravel模型、控制器等的集成(以使文档更接近代码)