Skip to content

Commit 9e67aec

Browse files
committed
document enums as resource
1 parent ba8d387 commit 9e67aec

File tree

3 files changed

+284
-0
lines changed

3 files changed

+284
-0
lines changed

core/enums.md

Lines changed: 281 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,281 @@
1+
# Enums as API Resources
2+
3+
API Platform provides support for PHP 8.1+ `BackedEnum`s, allowing them to be exposed as first-class API resources. This enables clients to discover available enum cases and their associated metadata directly through your API.
4+
5+
## Exposing BackedEnums
6+
7+
To expose a `BackedEnum` as an API resource, simply apply the `#[ApiResource]` attribute to your enum class:
8+
9+
```php
10+
<?php
11+
// api/src/Enum/Status.php
12+
namespace App\Enum;
13+
14+
use ApiPlatform\Metadata\ApiResource;
15+
16+
#[ApiResource]
17+
enum Status: int
18+
{
19+
case DRAFT = 0;
20+
case PUBLISHED = 1;
21+
case ARCHIVED = 2;
22+
}
23+
```
24+
25+
By default, API Platform will automatically generate `GET` and `GET Collection` operations for your enum resource. The enum's `value` will be used as the identifier for individual enum cases.
26+
27+
### Default Operations and Identifiers
28+
29+
- **Collection**: `GET /statuses` will return a collection of all enum cases.
30+
- **Item**: `GET /statuses/{value}` will return a single enum case based on its `value`.
31+
32+
Example `GET /statuses` response:
33+
34+
```json
35+
[
36+
{
37+
"name": "DRAFT",
38+
"value": 0
39+
},
40+
{
41+
"name": "PUBLISHED",
42+
"value": 1
43+
},
44+
{
45+
"name": "ARCHIVED",
46+
"value": 2
47+
}
48+
]
49+
```
50+
51+
Example `GET /statuses/1` response:
52+
53+
```json
54+
{
55+
"name": "PUBLISHED",
56+
"value": 1
57+
}
58+
```
59+
60+
### Customizing Enum Resources
61+
62+
You can customize the behavior of enum resources using standard API Platform attributes.
63+
64+
#### Custom Identifier
65+
66+
If you wish to use a property other than `value` as the identifier, or to expose additional data, you can implement methods within your enum and mark them with `#[ApiProperty]`. For instance, to use the enum `name` as an identifier, you can implement `getId()`:
67+
68+
```php
69+
<?php
70+
// api/src/Enum/Audit.php
71+
namespace App\Enum;
72+
73+
use ApiPlatform\Metadata\ApiProperty;
74+
use ApiPlatform\Metadata\ApiResource;
75+
76+
#[ApiResource]
77+
enum Audit: string
78+
{
79+
case Pending = 'pending';
80+
case Passed = 'passed';
81+
case Failed = 'failed';
82+
83+
#[ApiProperty(identifier: true)]
84+
public function getId(): string
85+
{
86+
return $this->name;
87+
}
88+
}
89+
```
90+
#### Adding Custom Properties
91+
92+
You can add custom properties to your enum resource by defining public methods and marking them with `#[ApiProperty]`:
93+
94+
```php
95+
<?php
96+
// api/src/Enum/Status.php
97+
namespace App\Enum;
98+
99+
use ApiPlatform\Metadata\ApiProperty;
100+
use ApiPlatform\Metadata\ApiResource;
101+
102+
#[ApiResource]
103+
enum Status: int
104+
{
105+
case DRAFT = 0;
106+
case PUBLISHED = 1;
107+
case ARCHIVED = 2;
108+
109+
#[ApiProperty]
110+
public function getDescription(): string
111+
{
112+
return match ($this) {
113+
self::DRAFT => 'Article is not ready for public consumption',
114+
self::PUBLISHED => 'Article is publicly available',
115+
self::ARCHIVED => 'Article content is outdated or superseded',
116+
};
117+
}
118+
}
119+
```
120+
121+
With the above, `GET /statuses/0` might return:
122+
123+
```json
124+
{
125+
"name": "DRAFT",
126+
"value": 0,
127+
"description": "Article is not ready for public consumption"
128+
}
129+
```
130+
131+
#### Custom State Providers
132+
133+
For more advanced customization, you can implement custom state providers for your enum resources.
134+
A common pattern is to use a trait to provide common functionality:
135+
136+
```php
137+
<?php
138+
// api/src/Enum/EnumApiResourceTrait.php
139+
namespace App\Enum;
140+
141+
use ApiPlatform\Metadata\Operation;
142+
use BackedEnum;
143+
144+
trait EnumApiResourceTrait
145+
{
146+
public function getId(): string|int
147+
{
148+
return $this->value;
149+
}
150+
151+
public function getValue(): int|string
152+
{
153+
return $this->value;
154+
}
155+
156+
public static function getCases(): array
157+
{
158+
return self::cases();
159+
}
160+
161+
public static function getCase(Operation $operation, array $uriVariables): ?BackedEnum
162+
{
163+
$id = is_numeric($uriVariables['id']) ? (int) $uriVariables['id'] : $uriVariables['id'];
164+
165+
return array_reduce(self::cases(), static fn($c, BackedEnum $case) => $case->name === $id || $case->value === $id ? $case : $c, null);
166+
}
167+
}
168+
```
169+
170+
Then, apply the trait and specify the providers in your enum:
171+
172+
```php
173+
<?php
174+
// api/src/Enum/Audit.php
175+
namespace App\Enum;
176+
177+
use ApiPlatform\Metadata\ApiResource;
178+
use ApiPlatform\Metadata\Get;
179+
use ApiPlatform\Metadata\GetCollection;
180+
181+
#[ApiResource]
182+
#[GetCollection(provider: Audit::class.'::getCases')]
183+
#[Get(provider: Audit::class.'::getCase')]
184+
enum Audit: string
185+
{
186+
use EnumApiResourceTrait;
187+
188+
case Pending = 'pending';
189+
case Passed = 'passed';
190+
case Failed = 'failed';
191+
}
192+
```
193+
194+
### Enums as Property Values
195+
196+
When an enum is used as a property in another `ApiResource`, it will be serialized by its `value` by default.
197+
198+
Consider an `Article` resource with a `Status` enum property:
199+
200+
```php
201+
<?php
202+
// api/src/ApiResource/Article.php
203+
namespace App\ApiResource;
204+
205+
use ApiPlatform\Metadata\ApiResource;
206+
use App\Enum\Status; // Import your enum
207+
208+
#[ApiResource]
209+
class Article
210+
{
211+
public ?int $id = null;
212+
public ?string $title = null;
213+
public ?Status $status = null; // Enum property
214+
}
215+
```
216+
217+
The serialization of `Article` will include the `value` of the `Status` enum:
218+
219+
```json
220+
{
221+
"id": 1,
222+
"title": "Once Upon A Title",
223+
"status": 1
224+
}
225+
```
226+
227+
The OpenAPI schema will also correctly represent the enum as its backing type (`integer` for `Status`):
228+
229+
```json
230+
{
231+
"Article": {
232+
"type": "object",
233+
"properties": {
234+
"id": {
235+
"readOnly": true,
236+
"type": "integer"
237+
},
238+
"title": {
239+
"type": "string"
240+
},
241+
"status": {
242+
"type": "integer",
243+
"enum": [0, 1, 2] // Enum values derived from the BackedEnum
244+
}
245+
}
246+
}
247+
}
248+
```
249+
250+
### Referencing Enum Resources as Property Values
251+
252+
If you have exposed an enum as an `ApiResource` (e.g., `#[ApiResource]` on `Status` enum), and then use that enum as a property in another resource (e.g., `Article::$status`), API Platform will serialize it as an IRI (Internationalized Resource Identifier) by default.
253+
254+
```php
255+
<?php
256+
// api/src/ApiResource/Article.php
257+
namespace App\ApiResource;
258+
259+
use ApiPlatform\Metadata\ApiResource;
260+
use App\Enum\Status; // Assume Status is also an ApiResource
261+
262+
#[ApiResource]
263+
class Article
264+
{
265+
public ?int $id = null;
266+
public ?string $title = null;
267+
public ?Status $status = null; // This will now be an IRI
268+
}
269+
```
270+
271+
The serialization of `Article` will include an IRI for the `Status` enum:
272+
273+
```json
274+
{
275+
"id": 1,
276+
"title": "Once Upon A Title",
277+
"status": "/statuses/1"
278+
}
279+
```
280+
281+
If you prefer the enum to be serialized by its `value` instead of an IRI, even when it's an `ApiResource`, you might need to adjust your serialization context or create a custom normalizer if the default behavior doesn't suit your needs.

core/openapi.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -345,6 +345,7 @@ This will produce the following Swagger documentation:
345345
```
346346

347347
To pass a context to the OpenAPI **v2** generator, use the `swaggerContext` attribute (notice the prefix: `swagger` instead of `openapi`).
348+
For documentation on how to expose PHP 8.1+ Enums as API resources, refer to the [Enums documentation](enums.md).
348349

349350
## Disabling an Operation From OpenAPI Documentation
350351

core/serialization.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
# The Serialization Process
22

3+
For documentation on how to expose PHP 8.1+ Enums as API resources, refer to the [Enums documentation](enums.md).
4+
35
## Overall Process
46

57
API Platform embraces and extends the Symfony Serializer Component to transform PHP entities in (hypermedia) API responses.

0 commit comments

Comments
 (0)