1

I’m using L5 Swagger in a Laravel project to generate OpenAPI documentation. I’ve written a custom command (app/Console/Commands/GenerateSwaggerAnnotations.php) that parses controller methods and generates basic @OA\RequestBody annotations. It works well for simple inputs, but I'm struggling to support nested request bodies, especially arrays of objects.

For example, from this structure in rules():

return [
    'employee_id' => 'required|integer',
    'destinations' => 'required|array',
    'destinations.*.trip_destination_id' => 'required|integer',
    'destinations.*.details' => 'array',
    'destinations.*.details.*.trip_cost_category_id' => 'integer',
];

I’d like to generate this OpenAPI annotation automatically:

@OA\RequestBody(
    required=true,
    @OA\JsonContent(
        required={"employee_id", "destinations"},
        @OA\Property(property="employee_id", type="integer"),
        @OA\Property(
            property="destinations",
            type="array",
            @OA\Items(
                @OA\Property(property="trip_destination_id", type="integer"),
                @OA\Property(
                    property="details",
                    type="array",
                    @OA\Items(
                        @OA\Property(property="trip_cost_category_id", type="integer")
                    )
                )
            )
        )
    )
)

I'm trying to write a helper that parses the rules() method and builds this structure, but I'm stuck on how to interpret the . and * in keys like destinations.*.details.*.trip_cost_category_id.

How can I programmatically convert these kinds of rules into a correct nested OpenAPI schema?

Please note:

  • I'm limited to using L5 Swagger.

  • I can’t use Scribe or Scramble due to project restrictions.

  • I’m looking for a programmatic way to generate these annotations — not manual annotations.

Any guidance on how to properly parse the nested structure from rules() and generate the corresponding OpenAPI annotation would be appreciated.

Thanks!

1 Answer 1

0

You could use attributes to make your life easier. And create a class that accepts your form request as a parameter to get all of that information from it's rules method, and maybe other stuff from your formRequest itself.

use App\OpenApi\RequestBody;
use OpenApi\Attributes as OAT;


#[OAT\Post(
    path: '/employee-destinations',
    description: 'Your description here',
    security: [ ... ],
    requestBody: new RequestBody(StoreEmployeeDestinationsRequest::class),
    /* ... */
)]
public function store(StoreEmployeeDestinationsRequest $request)
{
    ...
}
namespace App\OpenApi;

use Attribute;
use Illuminate\Foundation\Http\FormRequest;
use OpenApi\Attributes\RequestBody as BaseRequestBody;

#[Attribute(Attribute::TARGET_CLASS | Attribute::TARGET_METHOD | Attribute::TARGET_PROPERTY | Attribute::TARGET_PARAMETER | Attribute::IS_REPEATABLE)]
class RequestBody extends BaseRequestBody
{
    public function __construct(string $formRequest)
    {
        $formRequest = new $formRequest;
        $rules = $formRequest->rules();

        $required = $this->isRequired($rules);
        $content = $this->getContent($rules);
        $description = $this->getDescription($formRequest); // optional

        // transform the rules array into the parameters the parent class expects
 
        parent::__construct(
            ref: null,
            request: null,
            description: $description,
            required: $required,
            content: $content,
            x: null,
            attachables: null,
        );
    }

    protected function isRequired(array $rules): bool
    {
        ...
    }

    protected function getContent(array $rules): array
    {
        ...
    }
}
Sign up to request clarification or add additional context in comments.

1 Comment

I'm not entirely sure if the approach is completely different when not using attributes but even if you use annotations, each annotation maps to a class in the end.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.