API Platform provides support for PHP 8.1+ BackedEnums, 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.
To expose a BackedEnum as an API resource, simply apply the #[ApiResource] attribute to your
enum class:
<?php
// api/src/Enum/Status.php
namespace App\Enum;
use ApiPlatform\Metadata\ApiResource;
#[ApiResource]
enum Status: int
{
case DRAFT = 0;
case PUBLISHED = 1;
case ARCHIVED = 2;
}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.
GET /statuses will return a collection of all enum cases.GET /statuses/{value} will return a single enum case based on its value.Example GET /statuses response:
[
{
"name": "DRAFT",
"value": 0
},
{
"name": "PUBLISHED",
"value": 1
},
{
"name": "ARCHIVED",
"value": 2
}
]Example GET /statuses/1 response:
{
"name": "PUBLISHED",
"value": 1
}You can customize the behavior of enum resources using standard API Platform attributes.
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():
<?php
// api/src/Enum/Audit.php
namespace App\Enum;
use ApiPlatform\Metadata\ApiProperty;
use ApiPlatform\Metadata\ApiResource;
#[ApiResource]
enum Audit: string
{
case Pending = 'pending';
case Passed = 'passed';
case Failed = 'failed';
#[ApiProperty(identifier: true)]
public function getId(): string
{
return $this->name;
}
}You can add custom properties to your enum resource by defining public methods and marking them with
#[ApiProperty]:
<?php
// api/src/Enum/Status.php
namespace App\Enum;
use ApiPlatform\Metadata\ApiProperty;
use ApiPlatform\Metadata\ApiResource;
#[ApiResource]
enum Status: int
{
case DRAFT = 0;
case PUBLISHED = 1;
case ARCHIVED = 2;
#[ApiProperty]
public function getDescription(): string
{
return match ($this) {
self::DRAFT => 'Article is not ready for public consumption',
self::PUBLISHED => 'Article is publicly available',
self::ARCHIVED => 'Article content is outdated or superseded',
};
}
}With the above, GET /statuses/0 might return:
{
"name": "DRAFT",
"value": 0,
"description": "Article is not ready for public consumption"
}For more advanced customization, you can implement custom state providers for your enum resources. A common pattern is to use a trait to provide common functionality:
<?php
// api/src/Enum/EnumApiResourceTrait.php
namespace App\Enum;
use ApiPlatform\Metadata\Operation;
use BackedEnum;
trait EnumApiResourceTrait
{
public function getId(): string|int
{
return $this->value;
}
public function getValue(): int|string
{
return $this->value;
}
public static function getCases(): array
{
return self::cases();
}
public static function getCase(Operation $operation, array $uriVariables): ?BackedEnum
{
$id = is_numeric($uriVariables['id']) ? (int) $uriVariables['id'] : $uriVariables['id'];
return array_reduce(self::cases(), static fn($c, BackedEnum $case) => $case->name === $id || $case->value === $id ? $case : $c, null);
}
}Then, apply the trait and specify the providers in your enum:
<?php
// api/src/Enum/Audit.php
namespace App\Enum;
use ApiPlatform\Metadata\ApiResource;
use ApiPlatform\Metadata\Get;
use ApiPlatform\Metadata\GetCollection;
#[ApiResource]
#[GetCollection(provider: Audit::class.'::getCases')]
#[Get(provider: Audit::class.'::getCase')]
enum Audit: string
{
use EnumApiResourceTrait;
case Pending = 'pending';
case Passed = 'passed';
case Failed = 'failed';
}When an enum is used as a property in another ApiResource, it will be serialized by its value by
default.
Consider an Article resource with a Status enum property:
<?php
// api/src/ApiResource/Article.php
namespace App\ApiResource;
use ApiPlatform\Metadata\ApiResource;
use App\Enum\Status; // Import your enum
#[ApiResource]
class Article
{
public ?int $id = null;
public ?string $title = null;
public ?Status $status = null; // Enum property
}The serialization of Article will include the value of the Status enum:
{
"id": 1,
"title": "Once Upon A Title",
"status": 1
}The OpenAPI schema will also correctly represent the enum as its backing type (integer for
Status):
{
"Article": {
"type": "object",
"properties": {
"id": {
"readOnly": true,
"type": "integer"
},
"title": {
"type": "string"
},
"status": {
"type": "integer",
"enum": [0, 1, 2] // Enum values derived from the BackedEnum
}
}
}
}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.
<?php
// api/src/ApiResource/Article.php
namespace App\ApiResource;
use ApiPlatform\Metadata\ApiResource;
use App\Enum\Status; // Assume Status is also an ApiResource
#[ApiResource]
class Article
{
public ?int $id = null;
public ?string $title = null;
public ?Status $status = null; // This will now be an IRI
}The serialization of Article will include an IRI for the Status enum:
{
"id": 1,
"title": "Once Upon A Title",
"status": "/statuses/1"
}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.
You can also help us improve the documentation of this page.
Made with love by
Les-Tilleuls.coop can help you design and develop your APIs and web projects, and train your teams in API Platform, Symfony, Next.js, Kubernetes and a wide range of other technologies.
Learn more