diff --git a/docs/openapi/components.mdx b/docs/openapi/components.mdx
index 147f51b..317a8c5 100644
--- a/docs/openapi/components.mdx
+++ b/docs/openapi/components.mdx
@@ -153,15 +153,15 @@ $components = Components::create()
);
```
-Reference them from operations with `Response::ref()`. A reference carries no status code, so pass it to `response()` along with the code it answers:
+Reference them from operations with `->ref()`, which keeps the status code from the named constructor:
```php
Operation::get()
->responses(
Response::ok()->json(Reference::schema('User')),
- )
- ->response(404, Response::ref('NotFound'))
- ->response(401, Response::ref('Unauthorized'));
+ Response::notFound()->ref('NotFound'),
+ Response::unauthorized()->ref('Unauthorized'),
+ );
```
## Request Bodies
@@ -184,8 +184,8 @@ $components = Components::create()
);
// Reference
-Operation::post()->requestBody(RequestBody::ref('CreateUser'));
-Operation::patch()->requestBody(RequestBody::ref('UpdateUser'));
+Operation::post()->requestBody(Reference::requestBody('CreateUser'));
+Operation::patch()->requestBody(Reference::requestBody('UpdateUser'));
```
## Security Schemes
diff --git a/docs/openapi/paths-and-operations.mdx b/docs/openapi/paths-and-operations.mdx
index cd0c638..519151b 100644
--- a/docs/openapi/paths-and-operations.mdx
+++ b/docs/openapi/paths-and-operations.mdx
@@ -138,7 +138,7 @@ Operation::get()
### Attaching Responses
-`responses()` takes `Response` objects and keys them from their own status code, so you rarely repeat yourself. A `$ref` has no status code of its own, so pass those to `response()` with an explicit key:
+`responses()` takes `Response` objects and keys them from their own status code, so you rarely repeat yourself. Point a status code at a reusable response with `->ref()`:
```php
use Cortex\OpenApi\Objects\Response;
@@ -147,12 +147,17 @@ use Cortex\OpenApi\Objects\Reference;
Operation::get()
->responses(
Response::ok()->json(Reference::schema('Article')),
- )
- ->response(404, Response::ref('NotFound'))
- ->response(401, Response::ref('Unauthorized'));
+ Response::notFound()->ref('NotFound'),
+ );
```
-`responses()` replaces the whole map, so call it first and add any references with `response()` afterwards.
+`response()` sets a single status key, which is useful for a code with no named constructor or when adding to an existing map — `responses()` replaces the map wholesale:
+
+```php
+Operation::get()
+ ->responses(Response::ok()->json(Reference::schema('Article')))
+ ->response(418, Reference::response('Teapot'));
+```
See [Responses](/openapi/responses) for headers, links, and reusable response components.
diff --git a/docs/openapi/quickstart.mdx b/docs/openapi/quickstart.mdx
index 11f1dd8..fa852ba 100644
--- a/docs/openapi/quickstart.mdx
+++ b/docs/openapi/quickstart.mdx
@@ -277,14 +277,14 @@ Reference::example('AdminUser') // #/components/examples/AdminUser
Reference::callback('EventWebhook') // #/components/callbacks/EventWebhook
Reference::pathItem('LegacyPets') // #/components/pathItems/LegacyPets
-// Generic reference — use when the bucket isn't covered above
-Reference::to('#/components/schemas/Pet')
-
-// Shortcut on the target class (equivalent, and communicates intent)
-Response::ref('NotFound') // pass to Operation::response($status, ...)
-Parameter::ref('PetId')
-RequestBody::ref('CreateUser')
-Example::ref('AdminUser')
+// Generic reference — for a pointer outside the component buckets
+Reference::to('./common.yaml#/components/schemas/Pet')
+```
+
+Every reference is built here, so there is one place to look and one spelling to remember. The exception is a response inside `Operation::responses()`, which needs a status code to key on — see [Responses](/openapi/responses):
+
+```php
+Response::notFound()->ref('NotFound')
```
## Vendor Extensions
diff --git a/docs/openapi/request-bodies.mdx b/docs/openapi/request-bodies.mdx
index 5c23bfb..2d47de2 100644
--- a/docs/openapi/request-bodies.mdx
+++ b/docs/openapi/request-bodies.mdx
@@ -223,7 +223,7 @@ $components = Components::create()
// Reference from an operation
Operation::post()
->operationId('articles.create')
- ->requestBody(RequestBody::ref('CreateArticle'));
+ ->requestBody(Reference::requestBody('CreateArticle'));
```
## Complete Example
diff --git a/docs/openapi/responses.mdx b/docs/openapi/responses.mdx
index f4b6537..8bdbb5d 100644
--- a/docs/openapi/responses.mdx
+++ b/docs/openapi/responses.mdx
@@ -6,7 +6,7 @@ icon: 'file-down'
## Response
-`Response` describes a single HTTP status code outcome. Pass one or more of them to `Operation::responses()` and each is keyed from its own status code. References carry no status code, so attach those with `->response()`:
+`Response` describes a single HTTP status code outcome. Pass one or more of them to `Operation::responses()` and each is keyed from its own status code:
```php
use Cortex\OpenApi\Objects\Response;
@@ -18,13 +18,23 @@ Operation::get()
Response::ok()->json(Reference::schema('User')),
Response::notFound()->json(Reference::schema('Error')),
);
+```
+
+To answer a status code with a reusable response from `Components`, use `->ref()`. The status still comes from the named constructor, so it belongs in the same `responses()` call:
-// A reusable component response — the status code is the first argument
+```php
Operation::get()
- ->responses(Response::ok()->json(Reference::schema('User')))
- ->response(404, Response::ref('NotFound'));
+ ->responses(
+ Response::ok()->json(Reference::schema('User')),
+ Response::notFound()->ref('NotFound'),
+ Response::unauthorized()->ref('Unauthorized'),
+ );
```
+
+Elsewhere, references are built with `Reference::response('NotFound')` — see [Working with References](/openapi/quickstart). `->ref()` exists on `Response` because `responses()` keys each entry by status code, and a bare reference has none. Slots that supply the status themselves, such as `Components::response()` and `Operation::response(404, ...)`, take `Reference::response()`.
+
+
### Named Constructors
Every common HTTP status code has a named constructor that pre-populates the standard description. Override it with `->description()`.
@@ -202,9 +212,11 @@ $components = Components::create()
// Reference from an operation
Operation::get()
- ->responses(Response::ok()->json($schema))
- ->response(404, Response::ref('NotFound'))
- ->response(401, Response::ref('Unauthorized'));
+ ->responses(
+ Response::ok()->json($schema),
+ Response::notFound()->ref('NotFound'),
+ Response::unauthorized()->ref('Unauthorized'),
+ );
```
## Inline Examples on Responses
diff --git a/docs/openapi/webhooks-and-callbacks.mdx b/docs/openapi/webhooks-and-callbacks.mdx
index 02a3419..edeb4ca 100644
--- a/docs/openapi/webhooks-and-callbacks.mdx
+++ b/docs/openapi/webhooks-and-callbacks.mdx
@@ -211,7 +211,7 @@ Operation::post()
```php
Operation::post()
- ->callback('onEvent', Callback::ref('EventWebhook'))
+ ->callback('onEvent', Reference::callback('EventWebhook'))
->callback('onError', Callback::create()->expression(
'{$request.body#/errorUrl}',
PathItem::create('')->operations(
@@ -257,7 +257,7 @@ $components = Components::create()
Operation::post()
->operationId('hooks.subscribe')
->callbacks([
- 'onEvent' => Callback::ref('EventWebhook'),
+ 'onEvent' => Reference::callback('EventWebhook'),
]);
```
@@ -339,7 +339,7 @@ $doc = OpenApi::create()
)
->responses(Response::created())
->callbacks([
- 'onEvent' => Callback::ref('GenericWebhook'),
+ 'onEvent' => Reference::callback('GenericWebhook'),
]),
),
);
diff --git a/src/Objects/Callback.php b/src/Objects/Callback.php
index daa296c..a5814cb 100644
--- a/src/Objects/Callback.php
+++ b/src/Objects/Callback.php
@@ -22,11 +22,6 @@ public static function create(): self
return new self();
}
- public static function ref(string $name, ?string $summary = null, ?string $description = null): Reference
- {
- return Reference::callback($name, $summary, $description);
- }
-
public function expression(string $runtimeExpression, PathItem $pathItem): self
{
$this->expressions[$runtimeExpression] = $pathItem;
diff --git a/src/Objects/Example.php b/src/Objects/Example.php
index e4f3ee3..f6ea206 100644
--- a/src/Objects/Example.php
+++ b/src/Objects/Example.php
@@ -29,11 +29,6 @@ public static function create(): self
return new self();
}
- public static function ref(string $name, ?string $summary = null, ?string $description = null): Reference
- {
- return Reference::example($name, $summary, $description);
- }
-
public function summary(?string $summary): self
{
$this->summary = $summary;
diff --git a/src/Objects/Header.php b/src/Objects/Header.php
index ff1b60a..8ac0e1d 100644
--- a/src/Objects/Header.php
+++ b/src/Objects/Header.php
@@ -54,11 +54,6 @@ public static function create(): self
return new self();
}
- public static function ref(string $name, ?string $summary = null, ?string $description = null): Reference
- {
- return Reference::header($name, $summary, $description);
- }
-
public function description(?string $description): self
{
$this->description = $description;
diff --git a/src/Objects/Link.php b/src/Objects/Link.php
index aa30412..e195e3d 100644
--- a/src/Objects/Link.php
+++ b/src/Objects/Link.php
@@ -36,11 +36,6 @@ public static function create(): self
return new self();
}
- public static function ref(string $name, ?string $summary = null, ?string $description = null): Reference
- {
- return Reference::link($name, $summary, $description);
- }
-
public function operationRef(?string $operationRef): self
{
$this->operationRef = $operationRef;
diff --git a/src/Objects/Parameter.php b/src/Objects/Parameter.php
index c8310be..7bc1134 100644
--- a/src/Objects/Parameter.php
+++ b/src/Objects/Parameter.php
@@ -90,11 +90,6 @@ public static function cookie(string $name, JsonSchema|array|Reference|null $sch
return new self($name, In::Cookie, $schema);
}
- public static function ref(string $name, ?string $summary = null, ?string $description = null): Reference
- {
- return Reference::parameter($name, $summary, $description);
- }
-
public function getName(): string
{
return $this->name;
diff --git a/src/Objects/PathItem.php b/src/Objects/PathItem.php
index 95bc558..43c6e42 100644
--- a/src/Objects/PathItem.php
+++ b/src/Objects/PathItem.php
@@ -42,11 +42,6 @@ public static function create(string $path): self
return new self($path);
}
- public static function ref(string $name, ?string $summary = null, ?string $description = null): Reference
- {
- return Reference::pathItem($name, $summary, $description);
- }
-
public function getPath(): string
{
return $this->path;
diff --git a/src/Objects/RequestBody.php b/src/Objects/RequestBody.php
index 9cc8c5c..4615bf2 100644
--- a/src/Objects/RequestBody.php
+++ b/src/Objects/RequestBody.php
@@ -29,11 +29,6 @@ public static function create(): self
return new self();
}
- public static function ref(string $name, ?string $summary = null, ?string $description = null): Reference
- {
- return Reference::requestBody($name, $summary, $description);
- }
-
public function description(?string $description): self
{
$this->description = $description;
diff --git a/src/Objects/Response.php b/src/Objects/Response.php
index 8180b04..0983b03 100644
--- a/src/Objects/Response.php
+++ b/src/Objects/Response.php
@@ -35,6 +35,8 @@ final class Response implements Serializable, HasExtensionsInterface
private ?string $description;
+ private ?Reference $reference = null;
+
/**
* @var array
*/
@@ -126,9 +128,18 @@ public static function internalServerError(): self
return new self('500');
}
- public static function ref(string $name, ?string $summary = null, ?string $description = null): Reference
+ /**
+ * Answer this status code with a reusable response from Components.
+ *
+ * The status code is kept, so the result can go straight into Operation::responses().
+ * The document carries the $ref alone — a Reference Object has no room for the other
+ * fields set on this response.
+ */
+ public function ref(string $name, ?string $summary = null, ?string $description = null): self
{
- return Reference::response($name, $summary, $description);
+ $this->reference = Reference::response($name, $summary, $description);
+
+ return $this;
}
public function getStatusCode(): string
@@ -203,6 +214,10 @@ public function link(string $name, Link|Reference $link): self
*/
public function toArray(): array
{
+ if ($this->reference instanceof Reference) {
+ return $this->reference->toArray();
+ }
+
return $this->buildArray([
'description' => $this->description,
'headers' => $this->headers,
diff --git a/src/Objects/SecurityScheme.php b/src/Objects/SecurityScheme.php
index 640768f..ffef819 100644
--- a/src/Objects/SecurityScheme.php
+++ b/src/Objects/SecurityScheme.php
@@ -72,11 +72,6 @@ public static function mutualTls(): self
return new self(SecuritySchemeType::MutualTls);
}
- public static function ref(string $name, ?string $summary = null, ?string $description = null): Reference
- {
- return Reference::securityScheme($name, $summary, $description);
- }
-
public function getType(): SecuritySchemeType
{
return $this->securitySchemeType;
diff --git a/tests/Unit/Objects/CallbackTest.php b/tests/Unit/Objects/CallbackTest.php
index a2da579..259daa1 100644
--- a/tests/Unit/Objects/CallbackTest.php
+++ b/tests/Unit/Objects/CallbackTest.php
@@ -37,9 +37,3 @@
],
]);
});
-
-it('supports ref() shortcut', function (): void {
- expect(Callback::ref('Webhook')->toArray())->toBe([
- '$ref' => '#/components/callbacks/Webhook',
- ]);
-});
diff --git a/tests/Unit/Objects/ExampleTest.php b/tests/Unit/Objects/ExampleTest.php
index 8ce7296..00df292 100644
--- a/tests/Unit/Objects/ExampleTest.php
+++ b/tests/Unit/Objects/ExampleTest.php
@@ -37,12 +37,6 @@
]);
});
-it('supports ref() shortcut', function (): void {
- expect(Example::ref('SampleUser')->toArray())->toBe([
- '$ref' => '#/components/examples/SampleUser',
- ]);
-});
-
it('clears a value when explicitly cleared', function (): void {
$example = Example::create()->value('a');
diff --git a/tests/Unit/Objects/HeaderTest.php b/tests/Unit/Objects/HeaderTest.php
index 78f4a3d..b6b97c4 100644
--- a/tests/Unit/Objects/HeaderTest.php
+++ b/tests/Unit/Objects/HeaderTest.php
@@ -82,12 +82,6 @@
]);
});
-it('supports ref() shortcut', function (): void {
- expect(Header::ref('RateLimitRemaining')->toArray())->toBe([
- '$ref' => '#/components/headers/RateLimitRemaining',
- ]);
-});
-
it('accepts a Style enum for style()', function (): void {
$header = Header::create()->style(Style::Simple);
diff --git a/tests/Unit/Objects/LinkTest.php b/tests/Unit/Objects/LinkTest.php
index 294ade0..5010ca3 100644
--- a/tests/Unit/Objects/LinkTest.php
+++ b/tests/Unit/Objects/LinkTest.php
@@ -47,12 +47,6 @@
]);
});
-it('supports ref() shortcut', function (): void {
- expect(Link::ref('Foo')->toArray())->toBe([
- '$ref' => '#/components/links/Foo',
- ]);
-});
-
it('inserts requestBody after parameters when parameters are present', function (): void {
$link = Link::create()
->operationId('users.create')
diff --git a/tests/Unit/Objects/OperationTest.php b/tests/Unit/Objects/OperationTest.php
index ba02f7a..aa2873a 100644
--- a/tests/Unit/Objects/OperationTest.php
+++ b/tests/Unit/Objects/OperationTest.php
@@ -153,6 +153,22 @@
expect($operation->toArray()['responses'])->toHaveKeys(['200', '404']);
});
+it('responses() accepts a referenced response and keys it by status', function (): void {
+ $operation = Operation::get()->responses(
+ Response::ok(),
+ Response::notFound()->ref('NotFound'),
+ );
+
+ expect($operation->toArray()['responses'])->toBe([
+ '200' => [
+ 'description' => 'OK',
+ ],
+ '404' => [
+ '$ref' => '#/components/responses/NotFound',
+ ],
+ ]);
+});
+
it('adds a response by explicit status key, accepting Response or Reference', function (): void {
$operation = Operation::get()
->response('200', Response::ok())
@@ -179,7 +195,7 @@
it('adds callbacks one at a time with callback()', function (): void {
$operation = Operation::post()
->callback('onData', Callback::create()->expression('{$url}', PathItem::create('/hook')))
- ->callback('onError', Callback::ref('OnError'));
+ ->callback('onError', Reference::callback('OnError'));
$arr = $operation->toArray();
expect($arr['callbacks'])->toHaveKey('onData');
diff --git a/tests/Unit/Objects/ParameterTest.php b/tests/Unit/Objects/ParameterTest.php
index 933b2b5..1fea150 100644
--- a/tests/Unit/Objects/ParameterTest.php
+++ b/tests/Unit/Objects/ParameterTest.php
@@ -109,12 +109,6 @@
]);
});
-it('supports ref() shortcut', function (): void {
- expect(Parameter::ref('PageSize')->toArray())->toBe([
- '$ref' => '#/components/parameters/PageSize',
- ]);
-});
-
it('required() defaults to true', function (): void {
expect(Parameter::query('test', Schema::string())->required()->toArray())->toMatchArray([
'required' => true,
diff --git a/tests/Unit/Objects/PathItemTest.php b/tests/Unit/Objects/PathItemTest.php
index 1359a52..e832a91 100644
--- a/tests/Unit/Objects/PathItemTest.php
+++ b/tests/Unit/Objects/PathItemTest.php
@@ -66,12 +66,6 @@
]);
});
-it('supports ref() shortcut', function (): void {
- expect(PathItem::ref('UserById')->toArray())->toBe([
- '$ref' => '#/components/pathItems/UserById',
- ]);
-});
-
it('emits vendor extensions', function (): void {
$pathItem = PathItem::create('/users')
->x('internal', true);
diff --git a/tests/Unit/Objects/RequestBodyTest.php b/tests/Unit/Objects/RequestBodyTest.php
index c1f6456..e7de989 100644
--- a/tests/Unit/Objects/RequestBodyTest.php
+++ b/tests/Unit/Objects/RequestBodyTest.php
@@ -56,12 +56,6 @@
]);
});
-it('supports ref() shortcut', function (): void {
- expect(RequestBody::ref('Create')->toArray())->toBe([
- '$ref' => '#/components/requestBodies/Create',
- ]);
-});
-
it('required() defaults to true', function (): void {
$requestBody = RequestBody::create()
->required()
diff --git a/tests/Unit/Objects/ResponseTest.php b/tests/Unit/Objects/ResponseTest.php
index cd716bc..d052008 100644
--- a/tests/Unit/Objects/ResponseTest.php
+++ b/tests/Unit/Objects/ResponseTest.php
@@ -80,9 +80,33 @@
]);
});
-it('supports ref() shortcut', function (): void {
- expect(Response::ref('Unauthorized')->toArray())->toBe([
- '$ref' => '#/components/responses/Unauthorized',
+it('refTo() serializes to the reference alone', function (): void {
+ expect(Response::notFound()->ref('NotFound')->toArray())->toBe([
+ '$ref' => '#/components/responses/NotFound',
+ ]);
+});
+
+it('refTo() keeps the status code so responses() can key it', function (): void {
+ expect(Response::notFound()->ref('NotFound')->getStatusCode())->toBe('404');
+});
+
+it('refTo() carries summary and description onto the reference', function (): void {
+ expect(Response::unauthorized()->ref('Unauthorized', 'Auth failed', 'Token missing or expired')->toArray())
+ ->toBe([
+ '$ref' => '#/components/responses/Unauthorized',
+ 'summary' => 'Auth failed',
+ 'description' => 'Token missing or expired',
+ ]);
+});
+
+it('refTo() replaces any fields set on the response itself', function (): void {
+ $response = Response::notFound()
+ ->description('Locally described')
+ ->json(Schema::object())
+ ->ref('NotFound');
+
+ expect($response->toArray())->toBe([
+ '$ref' => '#/components/responses/NotFound',
]);
});
@@ -97,7 +121,7 @@
it('adds links one at a time with link()', function (): void {
$response = Response::ok()
->link('self', Link::create()->operationId('users.show'))
- ->link('next', Link::ref('NextUser'));
+ ->link('next', Reference::link('NextUser'));
expect($response->toArray()['links'])->toHaveKeys(['self', 'next']);
});
diff --git a/tests/Unit/Objects/SecuritySchemeTest.php b/tests/Unit/Objects/SecuritySchemeTest.php
index 4b99a25..833c602 100644
--- a/tests/Unit/Objects/SecuritySchemeTest.php
+++ b/tests/Unit/Objects/SecuritySchemeTest.php
@@ -71,9 +71,3 @@
'type' => 'mutualTLS',
]);
});
-
-it('supports ref() shortcut', function (): void {
- expect(SecurityScheme::ref('OAuth2')->toArray())->toBe([
- '$ref' => '#/components/securitySchemes/OAuth2',
- ]);
-});