bambamboole/spectacular
Spectacular is a Laravel package by bambamboole that streamlines common development tasks with a clean, expressive API. Easy to install and configure, it helps teams build features faster while keeping the codebase tidy, readable, and easy to maintain.
Spectacular layers OpenAPI 3.1 features (composition, discriminators, webhooks, richer info blocks) on top of dedoc/scramble. It is a documentation package, so nothing ships to production traffic — but generated specs are usually consumed by clients, so treat regeneration as part of your normal workflow.
Upgrade path to 0.30.0 (do this first)
composer require dedoc/scramble:^0.13.48 # hard requirement in 0.30.0
composer require bambamboole/spectacular:^0.30.0
php artisan vendor:publish --tag="spectacular-config" --force # only if you have no local edits
php artisan config:clear && php artisan scramble:export # regenerate and diff the spec
The 0.30.0 breaking changes are narrow but unavoidable:
OneOf::setDiscriminator() now accepts Scramble's Discriminator, not Spectacular's own.InfoObject is removed — use Scramble's Info instead.< 0.13.48 is no longer supported (serialisation changed upstream).Where to look first
config/spectacular.php — the primary surface for info/servers/webhooks wiring.OneOf / AnyOf / AllOf usages — these are where the discriminator change bites.storage/api-docs/api.json (or wherever you export) — commit it and diff it on upgrade; it is the fastest way to see what a version bump actually did.First use case
A polymorphic endpoint that Scramble renders as a single blob: model the response with OneOf plus a discriminator so API clients can generate a typed union.
use Bambamboole\Spectacular\OpenApi\OneOf;
use Dedoc\Scramble\Support\Generator\Discriminator;
OneOf::make([CatResource::class, DogResource::class])
->setDiscriminator(
Discriminator::make('pet_type')->setMapping([ // Scramble's class as of 0.30.0
'cat' => CatResource::class,
'dog' => DogResource::class,
])
);
Then php artisan scramble:export and verify the union and discriminator.mapping appear in the exported JSON.
Pin Scramble alongside Spectacular. Spectacular is tightly coupled to Scramble's internal generator objects — 0.30.0 exists specifically because Scramble 0.13.48 changed serialisation. Add a floor in composer.json rather than relying on the transitive constraint:
"require-dev": {
"dedoc/scramble": "^0.13.48",
"bambamboole/spectacular": "^0.30.0"
}
If you cannot move Scramble yet, pin bambamboole/spectacular:0.29.* until you can — mixing 0.29 with Scramble 0.13.48 will produce broken specs or hard errors.
Discriminators live with the schema, not the endpoint. The common failure is putting mapping keys that don't match the component names Scramble generates. Use resource classes in the mapping (as above) rather than hand-written strings where possible, and keep the discriminator property present in every variant's schema.
Compose deeply, not broadly. Prefer small AllOf-merged base resources plus OneOf at the top level over one giant resource. Regeneration stays readable in diffs, and OpenAPI consumers handle inheritance better than flat duplication.
Deep query parameters now follow Scramble's config. With scramble.flatten_deep_query_parameters = true, filter parameters (e.g. filter[name]) are emitted as flattened names instead of nested objects. This is a spec-shape decision that will break generated clients if you flip it — decide once per project and assert it in a snapshot test:
// tests/Feature/ApiDocsTest.php
it('exports a stable spec', function () {
$this->artisan('scramble:export')->assertSuccessful();
expect(file_get_contents(storage_path('api-docs/api.json')))
->toMatchSnapshot();
});
Publish the spec in CI. Run scramble:export in a pipeline job, fail on a non-empty diff, and upload the artifact. This turns every upgrade (including 0.30.0's serialisation fix) into a reviewable PR rather than a surprise for downstream SDKs.
Custom info block without the removed class. Anything that previously used Spectacular's InfoObject now builds on Scramble's Info; set title/version/description there and let the config file drive servers and webhooks.
setDiscriminator() signature is the whole migration. Any code passing Spectacular's discriminator will now throw a TypeError. Grep for setDiscriminator and for Discriminator imports, and swap the namespace in one pass.InfoObject removals fail loudly at boot, not at generation. If the class is referenced in a service provider, you'll see a class-not-found during php artisan before you ever try to export. Clear config and route caches after the bump.$ref targets, nullable handling) without erroring. Always diff the exported JSON rather than trusting a green command exit code.flatten_deep_query_parameters requires Scramble's config to be published. If config/scramble.php is absent the setting has no effect and your filters stay nested — a classic "the feature doesn't work" report that is really a publish step.npx @redocly/cli lint) in CI; composition and discriminator mistakes are structurally invalid but visually plausible.How can I help you explore Laravel packages today?