Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Strut Laravel Package

calcinai/strut

Strut is a Laravel package by calcinai that provides a structured, schema-oriented foundation for building data-driven applications, making it easier to define resources, relationships, and management workflows with less boilerplate.

Deep Wiki
Context7

Getting Started

Strut is a plain PHP object model for OpenAPI/Swagger documents — there is no service provider, no facade, and no config file. Install it with Composer and start using the classes directly:

composer require calcinai/strut

Where to look first, in order:

  1. Calcinai\Strut\OpenApi — the OpenAPI 3.0 root document (what you'll use most).
  2. Calcinai\Strut\Swagger — the Swagger/OpenAPI 2.0 root document.
  3. Calcinai\Strut\Factory — parses an existing spec and picks the right version class for you.
  4. The generated model classes under src/ (Info, PathItem, Operation, Response, Schema, Components, …) — they mirror the OpenAPI schema tree almost 1:1.

The first useful thing to try is building a small spec by hand in php artisan tinker (or a throwaway command) to see how the object tree behaves:

use Calcinai\Strut\OpenApi;

$spec = new OpenApi();
$spec->info->title = 'My API';
$spec->info->version = '1.0.0';
$spec->paths['/users']->get->responses['200']->description = 'OK';

echo $spec->toJson(JSON_PRETTY_PRINT);

Once that round-trips into something a validator accepts, move on to loading an existing spec with Factory::getSpecFromJson($json) (or the array equivalent) and mutating it — that load → modify → dump cycle is the real day-to-day use case.

Implementation Patterns

Generate specs from your Laravel routes. This is the highest-leverage pattern. Walk Route::getRoutes(), and for each route create a path/operation on a shared OpenApi instance:

$spec = new OpenApi();
$spec->info->title = config('app.name');
$spec->info->version = '1.0.0';

foreach (Route::getRoutes() as $route) {
    $path = '/' . ltrim($route->uri(), '/');
    $method = strtolower($route->methods()[0]);
    $op = $spec->paths[$path]->{$method};
    $op->operationId = $route->getName();
    $op->responses['200']->description = 'OK';
}

Wrap it in an Artisan command (openapi:generate) that writes storage/app/openapi.json or public/openapi.json. Commit the output or serve it — either way, keep generation out of the request lifecycle.

Load, patch, and re-emit. For teams that hand-author a spec, Strut is best used as a post-processor: read the source file, inject environment-specific servers, stamp version/build metadata, splice in generated schemas, then write it back. This keeps the hand-authored file reviewable while allowing automation.

Use components for reuse, not duplication. Define reusable schemas once under $spec->components->schemas['User'] and reference them as strings ('$ref' => '#/components/schemas/User'). Strut does not resolve or inline $ref, so you get exactly what you wrote — which is usually what you want.

Cross-version conversion. The package models Swagger 2.0 and OpenAPI 3.0 as separate trees. Convert via the root classes (toSwagger() / toOpenApi() where available in your pinned version) or the Factory, then diff the output against the original before trusting it. Treat conversion as a one-way migration aid rather than a lossless transform.

Merge multiple specs. If you split docs per module, build each as its own OpenApi, then merge paths/components into a root document by iterating toArray() and assigning into the target. Doing it at the array level is often clearer than fighting object identity.

Snapshot tests. In Pest/PHPUnit, assert against $spec->toArray() rather than the JSON string — array comparisons give you readable diffs and avoid key-ordering noise. Pair this with an external validator (see Gotchas) in CI.

Gotchas and Tips

  • It's a 2019 library. OpenAPI 3.0 and Swagger 2.0 only. No 3.1 support (webhooks, jsonSchemaDialect, sibling $ref keywords). Do not point it at a 3.1 document and expect a faithful round-trip.
  • No validation, no $ref resolution. You can happily produce an invalid document (missing info.title, dangling $ref, malformed status codes). Validate the output with swagger-cli validate or cebe/php-openapi in CI; resolve refs with a separate tool if you need inlining.
  • Magic property access can create nodes. $spec->paths['/x']->get tends to instantiate intermediate objects. Always guard reads with isset() / array_key_exists before inspecting, or you'll silently mutate the spec while "reading" it.
  • Status codes are strings. Use $responses['200'], not $responses[200]. Mixing int and string keys produces duplicate-looking entries in JSON.
  • Empty arrays vs. empty objects. An empty responses or components can serialize as [] where the spec requires {}. Check the raw toJson() output, not just the PHP structure, and force object encoding where needed.
  • Conversion is lossy. Vendor extensions (x-*), ordering, and some 3.0-only constructs may not survive a Swagger ↔ OpenAPI conversion. Diff before and after, and keep the original under version control.
  • Vendor extensions are just properties — assign $spec->{'x-internal-id'} = '...' directly on the relevant node.
  • Serialization options matter. toJson(JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES) produces far more readable output for review; toArray() is the right input for diffing and merging.
  • PHP version drift. With a 2019 release, expect deprecation notices on PHP 8.x (dynamic properties, nullable handling). Pin your PHP/Composer constraints accordingly, or vendor a fork if you hit hard failures.
  • Debugging technique: dump json_encode($spec, JSON_PRETTY_PRINT) at each mutation step. The object graph is deep and it's easy to set a property on the wrong node (e.g. paths['/x']->get->responses vs. paths['/x']->responses).
  • Extension points are thin. There's no plugin/hook system; if you need custom behavior, subclass the generated model classes or post-process the array output rather than expecting an event API.
Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
shitoudev/permissionmanager
johannesclimacus/laravel-tui
hocvt/seotools
vitalybaev/sypexgeo
amphibee/themosis-ignition
gito-me/fast-excel
frankyso/laravel-ipaymu
geolize-studio/laravel-chile
boomerangui/chat
jinas/dhivatar
store-doc/laravel-storage-driver
plus-it/laravel-translation-manager
diephp/perhaps
diephp/telegraph
osmaviation/spreadsheet
microsoft/fluentui-system-icons
mmccook/php-json-canonicalization-scheme
middlewares/firewall
qubiqx/montapacking
nyoncode/laravel-package-console