Search by

rayronvictor / scramble-laravel-actions

rayronvictor

Scramble (dedoc/scramble) support for Laravel Actions (lorisleiva/laravel-actions): document actions used as controllers in your OpenAPI docs.

Package info

github.com/rayronvictor/scramble-laravel-actions

pkg:composer/rayronvictor/scramble-laravel-actions

Statistics

Installs: 10

Dependents: 0

Suggesters: 0

Stars: 0

Open Issues: 0

v0.1.1 2026-11-04 02:43 UTC

This package is auto-updated.

Last update: 2026-11-04 02:44:08 UTC


README

Document the actions you use as controllers with Laravel Actions in the OpenAPI documentation generated by Scramble.

Laravel Actions registers an action as an invokable controller (CreateTodo@__invoke) and only decides at request time which method actually runs, how the request is validated and what the response looks like. Scramble never runs your routes, so out of the box it documents the generic __invoke method from the AsController trait: no request body, no validation errors and no response.

This package teaches Scramble the Laravel Actions conventions.

Installation

composer require rayronvictor/scramble-laravel-actions

The service provider is registered automatically through package discovery. There is nothing else to configure.

Requirements

  • PHP 8.2+
  • Laravel 11, 12 or 13
  • dedoc/scramble ^0.13.44
  • lorisleiva/laravel-actions ^2.8

What gets documented

Given this action:

use Lorisleiva\Actions\ActionRequest;
use Lorisleiva\Actions\Concerns\AsAction;

/**
 * Create a todo.
 *
 * Stores a new todo for the current user.
 */
class CreateTodo
{
    use AsAction;

    public function getControllerMiddleware(): array
    {
        return ['auth:sanctum'];
    }

    public function authorize(ActionRequest $request): bool
    {
        return $request->user()->can('create', Todo::class);
    }

    public function rules(): array
    {
        return [
            // The title of the todo.
            'title' => ['required', 'string', 'max:255'],
            'done' => ['sometimes', 'boolean'],
        ];
    }

    public function handle(User $user, string $title, bool $done = false): Todo
    {
        return $user->todos()->create(compact('title', 'done'));
    }

    public function asController(ActionRequest $request): Todo
    {
        return $this->handle($request->user(), $request->validated('title'), $request->boolean('done'));
    }

    public function jsonResponse(Todo $todo): TodoResource
    {
        return new TodoResource($todo);
    }
}
Route::post('todos', CreateTodo::class);

Scramble now documents:

Laravel Actions convention Documentation
asController (or handle when there is no asController) Analyzed as the route method: path parameters, route model binding, request calls like $request->input('q') and the return type.
rules() Request body (or query parameters for GET/DELETE), as a CreateTodoRequest schema. Comments on the rules become descriptions, just like with a FormRequest.
rules(), withValidator(), afterValidator(), getValidator() 422 validation error response.
authorize() 403 authorization error response, unless it always returns true.
getControllerMiddleware() Middlewares like auth add the 401 response, as with regular controllers.
jsonResponse($result, $request) Response of the operation. The type returned by the route method is passed to it, so jsonResponse($todo) { return response()->json(['data' => $todo]); } is documented with the right data shape.
Class PHPDoc Summary and description of the operation when the route method has none. Tags from the class PHPDoc (like @response or @unauthenticated) apply too, while the route method ones take precedence.
Operation ID Ends with the action name (…createTodo) instead of …createTodo.handle, like Scramble does for __invoke.

Everything else keeps working as Scramble documents it: attributes like #[Group], #[ExcludeRouteFromDocs], #[Api] or #[Response], PHPDoc annotations, resources, models, etc. Put them on the route method (asController or handle) or on the class.

Routes pointing to an explicit method of an action, like Route::get('todos/export', [ExportTodos::class, 'export']), are documented like any other controller method, because Laravel Actions does not validate the request nor transform the response for them.

How rules are evaluated

rules() is called the same way Laravel Actions calls it: the action is resolved from the container and the dependencies of rules() are injected, with an ActionRequest whose HTTP method matches the route. When that fails (for example because the rules need a route parameter or the authenticated user), Scramble's static evaluation of the returned array is used instead, the same fallback it uses for form requests.

Multiple APIs

The package registers its extensions on Scramble's default API configuration, which every API registered afterwards with Scramble::registerApi() inherits. If you register an API before this package's service provider boots, configure it yourself:

use RayronVictor\ScrambleLaravelActions\ScrambleLaravelActions;

ScrambleLaravelActions::configure(Scramble::registerApi('v2', ['api_path' => 'api/v2']));

How it works

  • ActionRouteProvider decorates Scramble's route provider and, on a copy of each action route, swaps __invoke for the method Laravel Actions would call (asController, then handle). Your application routes are never modified.
  • ActionRulesParametersExtractor documents the request from the action rules(). Scramble's form request extractor ignores ActionRequest, whose rules() only proxies the action's.
  • PrepareActionOperationExtension runs before Scramble's operation extensions: it falls back to the class PHPDoc and attaches the validation and authorization errors of the action.
  • ActionResponseExtension runs after them: it documents the jsonResponse() result and cleans up the operation ID.

Testing

composer test

License

The MIT License (MIT). See LICENSE for more information.