从零写一个自动生成API文档的laravel扩展包

931 阅读2分钟

手把手教你从零开始写一个laravel扩展包,并发布到packagist,为世界的开源世界做出你自己的贡献

创建基本的目录及结构

  • 创建一个laravel项目
  • 在项目的根目录创建一个目录packages用于存储测试的扩展包,目录结果如下
packages
├── hanyun
│ └── swagger
│     └── src

  • 创建Commands目录用于生成console命令
  • 创建Controllers目录用于存储控制器
  • 创建config目录用于存储配置文件
  • 创建routes目录 用于存放我们的路由
  • 创建swagger-ui目录用于存放swagger的静态页面
  • 创建view目录用于存放显示UI的界面

引入swagger-ui

从swagger官网下载依赖文件,将disk下的文件拷贝到 packages/hanyun/swagger/src/swagger-ui/dist 下面

│         ├── swagger-ui
│         │ └── dist
│         │     ├── favicon-16x16.png
│         │     ├── favicon-32x32.png
│         │     ├── index.html
│         │     ├── oauth2-redirect.html
│         │     ├── swagger-ui-bundle.js
│         │     ├── swagger-ui-bundle.js.map
│         │     ├── swagger-ui-es-bundle-core.js
│         │     ├── swagger-ui-es-bundle-core.js.map
│         │     ├── swagger-ui-es-bundle.js
│         │     ├── swagger-ui-es-bundle.js.map
│         │     ├── swagger-ui-standalone-preset.js
│         │     ├── swagger-ui-standalone-preset.js.map
│         │     ├── swagger-ui.css
│         │     ├── swagger-ui.css.map
│         │     ├── swagger-ui.js
│         │     └── swagger-ui.js.map

创建swagger的配置文件,

文件位置 /packages/hanyun/swagger/src/config/swagger.php


<?php
/**
 * User=> Only
 * Time=> 16=>30
 */
return [
    "info" => [
        "title" => "laravel swagger",
        "description" => 'laravel swagger generate OpenApi',
        "termsOfService" => "http://swagger.io/terms/",
        "contact" => [
            "email" => "1355081829@qq.com"
        ],
        "license" => [
            "name" => "MIT",
            "url" => ""
        ],
        "version" => "1.0.0"
    ],
    "servers" => [
        [
            "url" => "/",
            "description" => "laravel swagger OpenApi host"
        ]
    ],
];

创建视图

文件位置 packages/hanyun/swagger/src/view/index.blade.php后面会把这个文件发布到laravel的view目录下面

<!-- HTML for static distribution bundle build -->
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8">
    <title>Swagger UI</title>
    <link rel="stylesheet" type="text/css" href="{{asset('swagger-ui/swagger-ui.css')}}" >
    <link rel="icon" type="image/png" href="{{asset('swagger-ui/favicon-32x32.png')}}" sizes="32x32" />
    <link rel="icon" type="image/png" href="{{asset('swagger-ui/favicon-16x16.png')}}" sizes="16x16" />
    <style>
      html
      {
        box-sizing: border-box;
        overflow: -moz-scrollbars-vertical;
        overflow-y: scroll;
      }

      *,
      *:before,
      *:after
      {
        box-sizing: inherit;
      }

      body
      {
        margin:0;
        background: #fafafa;
      }
    </style>
  </head>

  <body>
    <div id="swagger-ui"></div>

    <script src="{{asset('swagger-ui/swagger-ui-bundle.js')}}" charset="UTF-8"> </script>
    <script src="{{asset('swagger-ui/swagger-ui-standalone-preset.js')}}" charset="UTF-8"> </script>
    <script>
    window.onload = function() {
      // Begin Swagger UI call region
      const ui = SwaggerUIBundle({
        url: "{{asset('swagger-ui/swagger.json')}}",
        dom_id: '#swagger-ui',
        deepLinking: true,
        presets: [
          SwaggerUIBundle.presets.apis,
          SwaggerUIStandalonePreset
        ],
        plugins: [
          SwaggerUIBundle.plugins.DownloadUrl
        ],
        layout: "StandaloneLayout"
      })
      // End Swagger UI call region

      window.ui = ui
    }
  </script>
  </body>
</html>

创建控制器,用于显示文档界面

文件位置 packages/hanyun/swagger/src/Controllers/SwaggerController.php


<?php

namespace Hanyun\Swagger\Controllers;

use App\Http\Controllers\Controller;
use Illuminate\Http\Request;

class SwaggerController extends Controller
{

    //
    public function index()
    {
        return view('swagger-ui.index');
    }
}



创建路由

文件位置 packages/hanyun/swagger/src/routes/swagger.php


<?php
/**
 * User: Only
 * Time: 17:04
 */

use Illuminate\Support\Facades\Route;

Route::get('/swagger', [\Hanyun\Swagger\Controllers\SwaggerController::class, 'index']);


创建console命令用于生成文档

文件位置 packages/hanyun/swagger/src/Commands/Swagger.php


<?php

namespace App\Console\Commands;

use Illuminate\Console\Command;

class Swagger extends Command
{
    /**
     * The name and signature of the console command.
     *
     * @var string
     */
    protected $signature = 'swagger:generate';

    /**
     * The console command description.
     *
     * @var string
     */
    protected $description = 'Generate swagger API';

    /**
     * Create a new command instance.
     *
     * @return void
     */
    public function __construct()
    {
        parent::__construct();
    }

    /**
     * Execute the console command.
     *
     * @return int
     */
    public function handle()
    {

        $openapi = \OpenApi\scan(app_path() . '/Http/Controllers');
        header('Content-Type: application/json');
        $json = $openapi->toJson();
        $json = preg_replace('/\s{0,}\*\s{0,}#\s{0,}/', '# ', $json);
        $json = preg_replace('/\s{0,}\\\\r\\\\n\s{0,}/', '\n', $json);
        $json = preg_replace('/\s{0,}\*\s{0,}/', '', $json);
        $arr = json_decode($json, true);
        $arr['info'] = config('swagger.info');
        $arr['servers'] = config('swagger.servers');
        $json = json_encode($arr);
        file_put_contents(public_path('swagger-ui/swagger.json'), $json);
        return 0;
    }
}



创建门面

文件位置 packages/hanyun/swagger/src/Facades/Swagger.php


<?php
/**
 * User: Only
 * Time: 16:39
 */

namespace Hanyun\Swagger\Facades;

use Illuminate\Support\Facades\Facade;

class Swagger extends Facade
{
    protected static function getFacadeAccessor()
    {
        return 'swagger';
    }
}


创建provider用于发布扩展

文件位置 packages/hanyun/swagger/src/SwaggerProvider.php


<?php

namespace App\Console\Commands;

use Illuminate\Console\Command;

class Swagger extends Command
{
    /**
     * The name and signature of the console command.
     *
     * @var string
     */
    protected $signature = 'swagger:generate';

    /**
     * The console command description.
     *
     * @var string
     */
    protected $description = 'Generate swagger API';

    /**
     * Create a new command instance.
     *
     * @return void
     */
    public function __construct()
    {
        parent::__construct();
    }

    /**
     * Execute the console command.
     *
     * @return int
     */
    public function handle()
    {

        $openapi = \OpenApi\scan(app_path() . '/Http/Controllers');
        header('Content-Type: application/json');
        $json = $openapi->toJson();
        $json = preg_replace('/\s{0,}\*\s{0,}#\s{0,}/', '# ', $json);
        $json = preg_replace('/\s{0,}\\\\r\\\\n\s{0,}/', '\n', $json);
        $json = preg_replace('/\s{0,}\*\s{0,}/', '', $json);
        $arr = json_decode($json, true);
        $arr['info'] = config('swagger.info');
        $arr['servers'] = config('swagger.servers');
        $json = json_encode($arr);
        file_put_contents(public_path('swagger-ui/swagger.json'), $json);
        return 0;
    }
}


修改我们创建的扩展包下面的 composer.json


{
    "name": "hanyun/swagger",
    "description": "Swagger for laravel",
    "keywords": [
        "swagger",
        "laravel",
        "openapi"
    ],
    "license": "MIT",
    "authors": [
        {
            "name": "hanyun",
            "email": "1355081829@qq.com"
        }
    ],
    "autoload": {
        "psr-4": {
            "Hanyun\\Swagger\\": "src/"
        }
    },
    "require": {
        "php": "^7.3",
        "zircote/swagger-php": "^3.1"
    }
}


修改我们创建的laravel项目下的composer.json

"Hanyun\\Swagger\\": "packages/hanyun/swagger/src" 让我们的项目可以引入我们的扩展包做测试,测试通过之后我们可以把我们的扩展包发布到GitHub上面,然后再发布到packagist.org,这样其他人就可以通过composer引入你的扩展包

    "autoload": {
        "psr-4": {
            "App\\": "app/",
            "Hanyun\\Swagger\\": "packages/hanyun/swagger/src",
            "Database\\Factories\\": "database/factories/",
            "Database\\Seeders\\": "database/seeders/"
        }
    }

最终目录结果如下:

packages
├── hanyun
│ └── swagger
│     └── src
│         ├── Commands
│         │ └── Swagger.php
│         ├── Controllers
│         │ └── SwaggerController.php
│         ├── Facades
│         │ └── Swagger.php
│         ├── Swagger.php
│         ├── SwaggerProvider.php
│         ├── config
│         │ └── swagger.php
│         ├── routes
│         │ └── swagger.php
│         ├── swagger-ui
│         │ └── dist
│         │     ├── favicon-16x16.png
│         │     ├── favicon-32x32.png
│         │     ├── index.html
│         │     ├── oauth2-redirect.html
│         │     ├── swagger-ui-bundle.js
│         │     ├── swagger-ui-bundle.js.map
│         │     ├── swagger-ui-es-bundle-core.js
│         │     ├── swagger-ui-es-bundle-core.js.map
│         │     ├── swagger-ui-es-bundle.js
│         │     ├── swagger-ui-es-bundle.js.map
│         │     ├── swagger-ui-standalone-preset.js
│         │     ├── swagger-ui-standalone-preset.js.map
│         │     ├── swagger-ui.css
│         │     ├── swagger-ui.css.map
│         │     ├── swagger-ui.js
│         │     └── swagger-ui.js.map
│         └── view
│             └── index.blade.php



测试

1、项目的/config/app.php 的providers数组里面添加 \Hanyun\Swagger\SwaggerProvider::class 如下所示

```
    'providers' => [
        //...其他的依赖
        \Hanyun\Swagger\SwaggerProvider::class
    ],

```

2、项目的/config/app.php 的aliases数组里面添加 'swagger'=>\Hanyun\Swagger\Facades\Swagger::class 如下所示

    'aliases' => [
        //...   其他的省略
        'swagger'=>\Hanyun\Swagger\Facades\Swagger::class
    ],

3、在项目根目录执行 php artisan vendor:publish , 找到 [4 ] Provider: Hanyun\Swagger\SwaggerProvider 这一行,输入前面的数字,按回车

4、执行 php artian make:controller Api/v1/IndexController 生成控制器,修改代码


<?php

namespace App\Http\Controllers\Api\v1;

use App\Http\Controllers\Controller;
use Illuminate\Http\Request;
use OpenApi\Annotations as OA;

class IndexController extends Controller
{
    //
    /**
     * @OA\Get(
     *     path="/api/index",
     *   security={{
     *     "api_key":{}
     *   }},
     *     @OA\Response(response="200", description="
     *      |参数|说明|备注||||
     *      |:---:|:---:|:---:|-----|-----|-----|
     *      |status|状态|['已取消', '等待付款', '下单成功', '付款中'] 取数组索引||||
     *     ")
     * )
     */
    public function index(Request $request)
    {
        return $request->all();
    }
}


修改 App\Http\Controllers\Controller.php 代码如下


<?php

namespace App\Http\Controllers;

use Illuminate\Foundation\Auth\Access\AuthorizesRequests;
use Illuminate\Foundation\Bus\DispatchesJobs;
use Illuminate\Foundation\Validation\ValidatesRequests;
use Illuminate\Routing\Controller as BaseController;
use OpenApi\Annotations as OA;


/**
 * @OA\OpenApi(
 *     @OA\Info(
 *         version="1.0.0",
 *         title="Swagger Petstore",
 *         description="This is a sample server Petstore server.  You can find out more about Swagger at [http://swagger.io](http://swagger.io) or on [irc.freenode.net, #swagger](http://swagger.io/irc/).  For this sample, you can use the api key `special-key` to test the authorization filters.",
 *         termsOfService="http://swagger.io/terms/",
 *         @OA\Contact(
 *             email="apiteam@swagger.io"
 *         ),
 *         @OA\License(
 *             name="Apache 2.0",
 *             url="http://www.apache.org/licenses/LICENSE-2.0.html"
 *         )
 *     ),
 *     @OA\Server(
 *         description="OpenApi host",
 *         url="https://petstore.swagger.io/v3"
 *     ),
 *     @OA\ExternalDocumentation(
 *         description="Find out more about Swagger",
 *         url="http://swagger.io"
 *     )
 * )
 * @OA\SecurityScheme(
 *   securityScheme="api_key",
 *   type="apiKey",
 *   in="header",
 *   name="Authorization"
 * )
 */
class Controller extends BaseController
{
    use AuthorizesRequests, DispatchesJobs, ValidatesRequests;
}

5、修改 config/swagger.php 这个会自动覆盖swagger的默认配置信息

6、在项目根目录执行 php artisan swagger:generate,生成API

7、项目根目录执行 php artisan serve,打开 项目文档

发布我们的扩展包

1、提交到GitHub上面

2、发布到ackagist.org

打开ackagist.org 输入你的扩展包的GitHub地址,点击check,就生成了扩展包

具体的swagger文档zircote/swagger-php文档

github地址 欢迎大家star和fork