goldspecdigital/oooas
Dependency-free PHP library for building OpenAPI specs with immutable, strongly-typed objects. Compose info, paths, operations, schemas, responses, and tags in code, then export the finished specification to JSON (YAML via another package).
## Getting Started
### Minimal Setup
1. **Installation**
```bash
composer require goldspecdigital/oooas:^2.10
Add to composer.json if not using autoloading:
"autoload": {
"psr-4": {
"App\\": "app/",
"Oooas\\": "vendor/goldspecdigital/oooas/src/"
}
}
Run composer dump-autoload.
First Use Case: Basic API Definition with Schema Contracts
Create a simple API class extending Oooas\OpenApi\OpenApi and leverage the new SchemaContract support:
use Oooas\OpenApi\OpenApi;
use Oooas\OpenApi\Schema;
class MyApi extends OpenApi
{
public function init(): void
{
$this->info->title = 'My API';
$this->info->version = '1.0.0';
// Define a schema using SchemaContract (new in v2.10)
$this->components->schemas->user = new class extends Schema {
public function init(): void
{
$this->type = 'object';
$this->properties = [
'id' => new class extends Schema {
public function init(): void {
$this->type = 'integer';
}
},
'name' => new class extends Schema {
public function init(): void {
$this->type = 'string';
}
},
];
}
};
}
}
Generate Spec
$api = new MyApi();
$api->generate();
echo $api->toJson();
Oooas\OpenApi\OpenApi (base class)Oooas\OpenApi\Schema (now supports SchemaContract instances in properties())Oooas\OpenApi\MediaType (for request/response bodies)Oooas\OpenApi\Operation (for endpoints)Use middleware to auto-generate OpenAPI specs from Laravel routes (unchanged):
// app/Http/Middleware/DocumentApi.php
public function handle($request, Closure $next)
{
if ($request->path() === 'api/docs') {
$api = new MyApi();
$api->generate();
return response()->json($api->toArray());
}
return $next($request);
}
Map Laravel routes to OpenAPI operations (unchanged):
use Oooas\OpenApi\Operation;
class MyApi extends OpenApi
{
public function init(): void
{
$this->path('/users', function (Operation $path) {
$path->get(function (Operation $operation) {
$operation->summary = 'Get all users';
$operation->operationId = 'getUsers';
$operation->responses->{'200'} = new \stdClass();
});
});
}
}
Leverage the new SchemaContract support for nested schemas:
// app/OpenApi/Components/UserSchema.php
use Oooas\OpenApi\Schema;
class UserSchema extends Schema
{
public function init(): void
{
$this->type = 'object';
$this->properties = [
'id' => new class extends Schema {
public function init(): void {
$this->type = 'integer';
$this->format = 'int64';
}
},
'name' => new class extends Schema {
public function init(): void {
$this->type = 'string';
$this->maxLength = 100;
}
},
'address' => new AddressSchema(), // Another SchemaContract
];
}
}
Define JWT or API key security globally:
$this->components->securitySchemes->bearerAuth = [
'type' => 'http',
'scheme' => 'bearer',
'bearerFormat' => 'JWT',
];
Use zircote/swagger-php to validate generated specs:
composer require zircote/swagger-php
use Zircote\Swagger\Validator;
$validator = new Validator();
$results = $validator->validate($api->toArray());
Test nested SchemaContract instances:
public function testUserSchemaWithContracts()
{
$schema = new UserSchema();
$this->assertInstanceOf(Schema::class, $schema->properties['id']);
$this->assertEquals('int64', $schema->properties['id']->format);
}
SchemaContract Initialization Order
SchemaContract instances are properly initialized in init().Operation ID Conflicts (Unchanged)
operationId is unique across all endpoints.Media Type Mismatches (Unchanged)
MediaType for request/response bodies. Ensure schema is properly set:
$response->content->{'application/json'} = new MediaType();
$response->content->{'application/json'}->schema = new UserSchema();
Server URL Handling (Unchanged)
servers. Use Laravel’s url() helper.Inspect Schema Contracts Dump nested schemas to verify initialization:
dd($api->components->schemas->user->properties['address']);
Validate Incrementally (Unchanged) Test components (schemas, paths) in isolation before combining them.
Leverage Laravel’s dd() (Unchanged)
Debug complex objects:
dd($api->paths->{'/users'}->get->responses);
Custom Schema Contracts Create reusable contracts for complex nested structures:
class PaginationSchema extends Schema {
public function init(): void {
$this->type = 'object';
$this->properties = [
'total' => new class extends Schema {
public function init(): void { $this->type = 'integer'; }
},
'per_page' => new class extends Schema {
public function init(): void { $this->type = 'integer'; }
},
];
}
}
Plugin System (Unchanged) Create plugins for auto-generating specs from Laravel controllers.
Event System (Unchanged)
Hook into Oooas\OpenApi\Events to modify the spec dynamically.
Route Caching (Unchanged)
If using php artisan route:cache, regenerate the OpenAPI spec manually.
Dynamic Routes (Unchanged)
Handle dynamic segments (e.g., {id}) in paths.
Middleware Integration (Unchanged) Exclude middleware-heavy routes from OpenAPI docs.
Cache Generated Specs (Unchanged) Store the JSON output in Laravel’s cache.
Lazy-Load Schema Contracts
Defer loading heavy contracts until needed (e.g., only include PaginationSchema for paginated endpoints).
Schema::properties() method now expects SchemaContract instances for nested schemas. Ensure all schema definitions are updated to use the new syntax:
// Old (deprecated)
$this->properties = ['id' => ['type' => 'integer']];
// New (v2.10+)
$this->properties = ['id' => new class extends Schema {
public function init(): void { $this->type = 'integer'; }
}];
NO_UPDATE_NEEDED would not apply here due to the breaking change in schema handling.
How can I help you explore Laravel packages today?