PlatoAdmin

API 契约与代码生成

api 应用的接口以 api/contracts/*.php 为源。lumnd/plato-api-contract 生成控制器与 OpenAPI, api/app/logic/ 是项目代码,生成器只在文件不存在时创建骨架,之后不会覆盖。

目录

位置 归属 说明
api/contracts/*.php 人写 DSL 契约与轻量 schema
api/app/logic/*.php 人写 业务入口,接收验证后的数组和 ApiContext
api/app/control/ctl_*.php 生成 路由、输入读取、验证与 Logic 调用
docs/api/openapi.json 生成 OpenAPI 3.1 文档
docs/api/index.html 人写 读上面那份文档的 Swagger UI 页,开发期给前端调试用
api/manifest.json 生成 生成物指纹和所有权

工具专用目录只有 contracts。项目不再维护 app/contractapp/dtoapp/generated,也不覆盖 生成器模板——响应形状是配置回答的问题,不是抄一份模板改(见下)。

配置与命令

三个命令共用根 plato.config.phpapi_contract

'api_contract' => [
    'contracts'            => $root . '/api/contracts',
    'output'               => $root,
    'controller-namespace' => 'api\\control',
    'logic-namespace'      => 'api\\logic',
    'controller-dir'       => 'api/app/control',
    'logic-dir'            => 'api/app/logic',
    'exception'            => common\exception\biz_exception::class,
    'openapi'              => 'docs/api/openapi.json',
    'manifest'             => 'api/manifest.json',
],
docker exec php83 sh -lc 'cd /data/web/platoadmin && vendor/bin/plato api:lint'
docker exec php83 sh -lc 'cd /data/web/platoadmin && vendor/bin/plato api:generate'
docker exec php83 sh -lc 'cd /data/web/platoadmin && vendor/bin/plato api:check'

独立的 vendor/bin/plato-api 仍支持 --config=/任意位置/options.php。显式配置文件可直接返回 api_contract 的选项数组,也可返回包含 api_contract 的完整 Plato 配置。

Schema 与 Logic

本项目使用 rules() 直接声明输入输出,写法与 Laravel FormRequest 一致,不再为纯数据搬运创建 DTO。控制器把读到的输入投影成数组传给 Logic,Logic 返回符合响应 schema 的数组。确实需要强类型 领域边界的项目仍可把 readonly DTO class 传给 endpoint,生成器会继续负责构造和投影。

请求的每个字段必须声明 requirednullabledefault: 之一,否则 lint 报 rules.presence_undeclared——「字段缺席时 Logic 拿到什么」要写出来,不能靠默认行为猜。布尔字段 不能 requiredvalidatefalse 读成没传,两者无法区分,只能给 default:nullable

投影的来源是控制器读到的 $input,不是 $validator->validated():后者只返回带了规则的字段并且丢掉 null,拿它投影会让「没有任何规则要检查」的字段静默消失。

生成的控制器怎么答

参数错误抛 biz_exception plato.config.phpexception 注册了它,生成的 action 因此写成 throw \common\exception\biz_exception::refuse($validator->errors());——参数错误和业务拒绝走同一条 路,都由 common\middleware\catcher 渲染成 {code:-2, msg, data:{errors}},本项目没有第二种失败 机制,控制器里也不再有信封。这条契约是 Lumnd\PlatoApiContract\Runtime\Refusalbiz_exception 实现的那个静态工厂;api:generate 在写任何文件之前先检查它,类名写错是配置错误而不是运行期崩溃。

成功答 resp::response(0, $data) 框架自己的信封,msg 交给框架填 successful,不再是 code::message(code::OK)——成功的那句话在生成期没法多语言化,要么写死一种语言,要么交给框架。要拿回 本地化就得覆盖 action 模板,那是另一件事,本项目目前不做。

认证

契约只声明 auth,取值 required(默认)、optionalnone。公开接口显式写 auth: 'none'。 该值直接生成到控制器 $actions

'logout' => [
    'methods' => ['POST'],
    'auth' => 'required',
],

PlatoPHP 按这个值决定 check_purview_handle 怎么跑:none 根本不调用,optional 调用但允许答 「没人」,required 调用且必须拿到身份,拿不到框架自己答 401。本项目 api 只用 nonerequired。因此没有 purview 配置、api_access.php、permission DSL 或控制器内的重复鉴权。

当前契约包含:

方法与路径 需要登录 Logic
POST /auth/send_code api\\logic\\auth_send_code
POST /auth/login api\\logic\\auth_login
POST /auth/refresh api\\logic\\auth_refresh
POST /auth/logout api\\logic\\auth_logout