You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何从Laravel FormRequest规则自动生成嵌套OpenAPI请求体?

解决方案:解析Laravel验证规则生成嵌套OpenAPI注解

核心思路

要处理带.*的嵌套规则,需要分两步实现:

  1. 将扁平的规则键(如destinations.*.details.*.trip_cost_category_id)转换为嵌套的Schema树结构
  2. 递归遍历Schema树,生成符合L5 Swagger规范的@OA注解字符串

第一步:构建嵌套Schema树

先实现一个工具函数,把扁平规则解析成层级化的结构,同时标记必填字段:

/**
 * 将Laravel验证规则转换为嵌套Schema树
 * @param array $rules 控制器的rules数组
 * @return array 包含schema树和顶层必填字段的数组
 */
function buildSchemaTree(array $rules): array
{
    $tree = [];
    $topLevelRequired = [];

    foreach ($rules as $key => $ruleString) {
        $segments = explode('.', $key);
        $currentNode = &$tree;
        $isRequired = str_contains($ruleString, 'required');
        $fieldType = extractRuleType($ruleString);

        // 标记顶层必填字段
        if ($isRequired && count($segments) === 1) {
            $topLevelRequired[] = $key;
        }

        foreach ($segments as $index => $segment) {
            $isLastSegment = $index === count($segments) - 1;
            $isArrayItem = $segment === '*';

            if ($isArrayItem) {
                // 处理数组项:父节点标记为array类型,指向items子节点
                if (!isset($currentNode['type']) || $currentNode['type'] !== 'array') {
                    $currentNode['type'] = 'array';
                    $currentNode['items'] = [];
                }
                $currentNode = &$currentNode['items'];
            } else {
                // 处理普通属性节点
                if (!isset($currentNode[$segment])) {
                    $currentNode[$segment] = [];
                }

                if ($isLastSegment) {
                    $currentNode[$segment]['type'] = $fieldType;
                    // 标记嵌套属性的必填性
                    if ($isRequired && count($segments) > 1) {
                        $parentNode = &$tree;
                        // 回溯到父对象节点
                        for ($i = 0; $i < $index; $i++) {
                            $seg = $segments[$i];
                            $parentNode = $seg === '*' ? &$parentNode['items'] : &$parentNode[$seg];
                        }
                        $parentNode['required'][] = $segment;
                    }
                } else {
                    $currentNode = &$currentNode[$segment];
                }
            }
        }
        unset($currentNode);
    }

    return [
        'tree' => $tree,
        'required' => $topLevelRequired,
    ];
}

/**
 * 从规则字符串中提取字段类型
 * @param string $ruleString 如"required|integer:min=1"
 * @return string 字段类型(integer/string/array等)
 */
function extractRuleType(string $ruleString): string
{
    $validTypes = ['integer', 'string', 'array', 'boolean', 'numeric', 'date'];
    $rules = explode('|', $ruleString);

    foreach ($rules as $rule) {
        $baseRule = explode(':', $rule)[0];
        if (in_array($baseRule, $validTypes)) {
            return $baseRule;
        }
    }
    return 'string'; // 默认类型
}

第二步:生成L5 Swagger注解

基于Schema树递归生成注解字符串,处理缩进和嵌套结构:

/**
 * 生成完整的@OA\RequestBody注解
 * @param array $schemaTree 从buildSchemaTree获取的结构
 * @param array $topLevelRequired 顶层必填字段
 * @param int $indent 基础缩进量
 * @return string 注解字符串
 */
function generateOaRequestBody(array $schemaTree, array $topLevelRequired, int $indent = 4): string
{
    $indentStr = str_repeat(' ', $indent);
    $innerIndent = str_repeat(' ', $indent + 4);

    $annotation = "@OA\RequestBody(\n";
    $annotation .= "{$indentStr}required=true,\n";
    $annotation .= "{$indentStr}@OA\JsonContent(\n";

    // 添加顶层必填字段
    if (!empty($topLevelRequired)) {
        $requiredList = implode(', ', array_map(fn($f) => "\"{$f}\"", $topLevelRequired));
        $annotation .= "{$innerIndent}required={$requiredList},\n";
    }

    // 生成所有属性注解
    $annotation .= generateOaProperties($schemaTree, $indent + 4);

    $annotation .= "{$indentStr})\n";
    $annotation .= ")";

    return $annotation;
}

/**
 * 递归生成@OA\Property注解
 * @param array $node 当前Schema节点
 * @param int $indent 当前缩进量
 * @return string 属性注解片段
 */
function generateOaProperties(array $node, int $indent): string
{
    $indentStr = str_repeat(' ', $indent);
    $innerIndent = str_repeat(' ', $indent + 4);
    $properties = '';

    foreach ($node as $propName => $propData) {
        // 跳过内部标记字段
        if (in_array($propName, ['type', 'items', 'required'])) continue;

        $properties .= "{$indentStr}@OA\Property(\n";
        $properties .= "{$innerIndent}property=\"{$propName}\",\n";
        $properties .= "{$innerIndent}type=\"{$propData['type']}\",\n";

        // 处理数组类型的items
        if (isset($propData['items'])) {
            $properties .= "{$innerIndent}@OA\Items(\n";
            if (!empty($propData['items'])) {
                $properties .= generateOaProperties($propData['items'], $indent + 8);
                // 添加数组项的必填字段
                if (isset($propData['items']['required'])) {
                    $requiredList = implode(', ', array_map(fn($f) => "\"{$f}\"", $propData['items']['required']));
                    $properties .= str_repeat(' ', $indent + 8) . "required={$requiredList},\n";
                }
            }
            $properties .= "{$innerIndent})\n";
        }

        // 添加当前对象的必填字段
        if (isset($propData['required'])) {
            $requiredList = implode(', ', array_map(fn($f) => "\"{$f}\"", $propData['required']));
            $properties .= "{$innerIndent}required={$requiredList},\n";
        }

        $properties .= "{$indentStr}),\n";
    }

    // 移除最后一个多余的逗号
    return rtrim($properties, ",\n") . "\n";
}

在自定义命令中使用

在你的GenerateSwaggerAnnotations命令的handle方法中调用上述函数:

public function handle()
{
    // 示例:获取目标控制器的rules数组
    $controller = new \App\Http\Controllers\YourController();
    $rules = $controller->rules();

    // 构建Schema树
    $schemaData = buildSchemaTree($rules);
    // 生成注解
    $annotation = generateOaRequestBody($schemaData['tree'], $schemaData['required']);

    // 后续逻辑:将注解写入控制器方法上方(需自行实现文件读写逻辑)
    // 例如:找到方法位置,插入注解字符串
}

扩展优化点

  1. 支持更多规则:可以扩展extractRuleType函数,处理email(映射为format="email")、date_format(映射为format="date-time")等规则
  2. nullable处理:若规则包含nullable,在注解中添加nullable=true
  3. 数组验证规则:处理min:3/max:10等数组规则,添加minItems/maxItems属性
  4. 文件读写优化:使用Laravel的文件操作工具类,精准定位控制器方法位置插入注解

内容的提问来源于stack exchange,提问作者Inji Aliyeva

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.13 00:44:52