Common techniques
Annotation placement
You shouldn't place all annotations inside one big block, but scatter them throughout your codebase as close to the relevant source code as appropriate.
swagger-php
will scan your project and merge all meta-data into one @OA\OpenApi
annotation.
WARNING
As of swagger-php
v4 all annotations or attributes must be associated with a structural element (class
, method
, parameter
or enum
)
Context awareness
swagger-php
looks at the context of the annotation and will augment it with things like property name
, data type
(doctype and native type hints) as well as a couple other things.
This means in a lot of cases it is not necessary to explicitly document all details.
Example
<?php
/**
* @OA\Schema()
*/
class Product {
/**
* The product name,
* @var string
* @OA\Property()
*/
public $name;
}
Results in
openapi: 3.0.0
components:
schemas:
Product:
properties:
name:
description: "The product name"
type: string
type: object
As if you'd written
/**
* The product name
* @var string
*
* @OA\Property(
* property="name",
* type="string",
* description="The product name"
* )
*/
public $name;
Response media type
The @OA\MediaType
is used to describe the content:
/**
* @OA\Response(
* response=200,
* description="successful operation",
* @OA\MediaType(
* mediaType="application/json",
* @OA\Schema(ref="#/components/schemas/User"),
* )
* ),
*/
But because most API requests and responses are JSON, the @OA\JsonContent
allows you to simplify this by writing:
/**
* @OA\Response(
* response=200,
* description="successful operation",
* @OA\JsonContent(ref="#/components/schemas/User"),
* )
*/
During processing the @OA\JsonContent
unfolds to @OA\MediaType( mediaType="application/json", @OA\Schema(...)
and will generate the same output.
Using references - $ref
It's quite common that endpoints have some overlap in either their request or response data. To keep things DRY (Don't Repeat Yourself) the specification allows reusing components using $ref
's
/**
* @OA\Schema(
* schema="product_id",
* type="integer",
* format="int64",
* description="The unique identifier of a product in our catalog"
* )
*/
Results in:
openapi: 3.0.0
components:
schemas:
product_id:
description: "The unique identifier of a product in our catalog"
type: integer
format: int64
This doesn't do anything by itself, but now you can reference this fragment by its path in the document tree #/components/schemas/product_id
/**
* @OA\Property(ref="#/components/schemas/product_id")
*/
public $id;
Examples
There are more uses cases on how to use refs in the using-refs example.
Array parameters in query
Depending on style-values @OA\Parameter(in="query", name="param", ...)
might create urls like this: path?param=123¶m=abc
which do not work when reading the param values in PHP.
The solution is to change the name param
into param[]
which will create path?param[]=123¶m[]=abc
and results in a PHP array.
Vendor extensions
The specification allows for custom properties as long as they start with "x-". Therefore, all swagger-php annotations have an x
property which accepts an array (map) and will unfold into "x-" properties.
/**
* @OA\Info(
* title="Example",
* version="1.0.0",
* x={
* "some-name": "a-value",
* "another": 2,
* "complex-type": {
* "supported":{
* {"version": "1.0", "level": "baseapi"},
* {"version": "2.1", "level": "fullapi"},
* }
* }
* }
* )
*/
Results in:
openapi: 3.0.0
info:
title: Example
version: 1
x-some-name: a-value
x-another: 2
x-complex-type:
supported:
- version: "1.0"
level: baseapi
- version: "2.1"
level: fullapi
Enums
PHP 8.1 enums are supported in two main use cases.
Enum cases as value
Enum cases can be used as value in an enum
list just like a string
, integer
or any other primitive type.
Basic enum:
use OpenApi\Attributes as OAT;
enum Suit
{
case Hearts;
case Diamonds;
case Clubs;
case Spades;
}
class Model
{
#[OAT\Property(enum: [Suit::Hearts, Suit::Diamonds])]
protected array $someSuits;
}
Results in:
openapi: 3.0.0
components:
schemas:
Model:
properties:
someSuits:
type: array
enum:
- Hearts
- Diamonds
type: object
Backed enums
If the enum is a backed enum, the case backing value is used instead of the name.
Enum schemas
The simples way of using enums is to annotate them as Schema
. This allows you to reference them like any other schema in your spec.
use OpenApi\Attributes as OAT;
#[OAT\Schema()]
enum Colour
{
case GREEN;
case BLUE;
case RED;
}
#[OAT\Schema()]
class Product
{
#[OAT\Property()]
public Colour $colour;
}
Results in:
openapi: 3.0.0
components:
schemas:
Colour:
type: string
enum:
- GREEN
- BLUE
- RED
Product:
properties:
colour:
$ref: '#/components/schemas/Colour'
type: object
Backed enums
For backed enums there exist two rules that determine whether the name or backing value is used:
- If no schema type is given, the enum name is used.
- If a schema type is given, and it matches the backing type, the enum backing value is used.
Using the name of a backed enum:
use OpenApi\Attributes as OAT;
#[OAT\Schema()]
enum Colour: int
{
case GREEN = 1;
case BLUE = 2;
case RED = 3;
}
#[OAT\Schema()]
class Product
{
#[OAT\Property()]
public Colour $colour;
}
Results in:
openapi: 3.0.0
components:
schemas:
Colour:
type: string
enum:
- GREEN
- BLUE
- RED
Product:
properties:
colour:
$ref: '#/components/schemas/Colour'
type: object
Using the backing value:
use OpenApi\Attributes as OAT;
#[OAT\Schema(type: 'integer')]
enum Colour: int
{
case GREEN = 1;
case BLUE = 2;
case RED = 3;
}
#[OAT\Schema()]
class Product
{
#[OAT\Property()]
public Colour $colour;
}
Results in:
openapi: 3.0.0
components:
schemas:
Colour:
type: integer
enum:
- 1
- 2
- 3
Product:
properties:
colour:
$ref: '#/components/schemas/Colour'
type: object