Add operationId in OpenAPI Specification using Scramble.

If you found this article, you most likely know what this is about, but I will write an intro anyway!

Skip the rambling

Scramble which is, in my opinon the best way to generate an OpenAPI Spec file for Laravel, without having to do additional work with annotating your controllers or change your code (aka, without being tedious, and making you just give up and do it manually like you thought about doing 3 hours ago, after all the packages you tried failed).

Anyways, over the weekend i got developer access to ChatGPT plugins, and I wanted to try creating a plugin for my Grocery price comparison site, that has an API, but no OpenAPI Spec, because spending 6 hours hand-writing the documentation was more fun then trying (and failing) to setup one of the generators you find when you search on Google for "laravel openapi generator", of which the first result is this one, which requires you to essentially replicate your entire FormRequest validation rules etc like this:

class ListUsersParameters extends ParametersFactory
{
    /**
     * @return Parameter[]
     */
    public function build(): array
    {
        return [

            Parameter::query()
                ->name('withTrashed')
                ->description('Display trashed users too')
                ->required(false)
                ->schema(Schema::boolean()),

        ];
    }
}

Ain't nobody got time for that.

So, I was looking for a solution that didn't drive me crazy, and I found Scramble after a bit of digging (actually found it on reddit of all places)...

I installed it, and boom, it worked instantly.

VICTORY!

But, there was no way to natively specify an operationId, which is required for ChatGPT Plugins, so I read through the docs and figured out how to do it with an Extension.

So here is how to do that:

Here is what you do.

1. Create a new Extension

I put mine in app/Extensions, but it does not matter, put it wherever makes sense for your project.

<?php

namespace App\Extensions;

use Dedoc\Scramble\Extensions\OperationExtension;
use Dedoc\Scramble\Support\Generator\Operation;
use Dedoc\Scramble\Support\RouteInfo;
use Illuminate\Support\Arr;

class AddOperationIdExtensionSupport extends OperationExtension
{
    public function handle(Operation $operation, RouteInfo $routeInfo)
    {
        if ($operationId = $routeInfo->phpDoc()->getTagsByName("@operationId")) {
            if ($value = trim(Arr::first($operationId)?->value?->value)) {
                $operation->setOperationId($value);
            }
        }
    }
}

2. Register your extension in the config/scramble.php config file:

Note: If you don't have this file in your config folder, you need to publish the configuration file by running this command:

php artisan vendor:publish --provider="Dedoc\Scramble\ScrambleServiceProvider" --tag="scramble-config"

Now register the extension in the "extensions" array.

<?php

use App\Extensions\AddOperationIdExtensionSupport;

return [
    // ...

    'extensions' => [
        AddOperationIdExtensionSupport::class,
    ],
];

3. Annotate your method with @operationId

Example:

<?php

use App\Http\Controllers\Controller;
use App\Http\Requests\Api\SearchProducts;
use App\Http\Resources\Api\ProductResource;
use App\Models\Product;

class ProductController extends Controller
{
    /**
     * Search for products
     *
     * Useful for searching for groceries and various product to find the price, ingredients and nutritional information
     *
     * @operationId searchProducts
     */
    public function index(SearchProducts $request)
    {
        return ProductResource::collection(
            Product::query()->paginate()
        );
    }
}

4. Done!

The endpoints in your OpenAPI specification now has an operationId, what a joy.

Originally posted this as a comment in this GitHub issue.