- Status: accepted
- Deciders: @dunglas, @soyuka, @vincentchalamon, @GregoireHebert
The API Platform @ApiResource
annotation was initially created to represent a Resource as defined in Roy Fielding's dissertation about REST in correlation with RFC 7231 about HTTP Semantics. This annotation brings some confusion as it mixes concepts of resources and operations. Here we discussed how we could revamp API Platform's resource definition using PHP8 attributes, being as close as we can to Roy Fielding's thesis vocabulary.
- Declare multiple ApiResource on a PHP Class see Subresources definition
- Declare operations in conjunction with resources using two attributes:
Resource
andOperation
- Use HTTP Verb to represent operations with a syntax sugar for collections (
CGET
?)
As Roy Fielding's thesis states:
REST uses a resource identifier to identify the particular resource involved in an interaction between components. REST connectors provide a generic interface for accessing and manipulating the value set of a resource, regardless of how the membership function is defined or the type of software that is handling the request.
In API Platform, this resource identifier is also named IRI (Internationalized Resource Identifiers). Following these recommendations, applied to PHP, we came up with the following PHP 8 attributes:
<?php
#[ApiResource]
class Users
{
#[ApiProperty(types="hydra:member")]
public iterable $member = [];
public float $averageRate;
}
#[ApiResource("/companies/{companyId}/users/{id}", normalizationContext=["groups"= [....]]), operations={}]
#[ApiResource(normalizationContext=["groups"= [....]], operations=[
new Get(),
new Post(),
])]
class User
{
#[ApiProperty(identifier=true)]
public $id;
}
Under the hood, API Platform would declare two routes Representing the /users
resource:
- GET /users
- POST /users
and three routes representing the /users/{id}
resource:
- GET /users/{id}
- PUT /users/{id}
- DELETE /users/{id}
For convenience and to ease the upgrade path, these would still be available on a single class:
<?php
#[ApiResource]
class User {}
corresponding to
<?php
#[Get]
#[GetCollection]
#[PostCollection]
#[Put]
#[Delete]
class User {}
Verbs declared on a PHP class define API Platform operations. The ApiResource
attributes would become optional and the only thing needed is to specify at least a verb and an IRI representing the Resource. Some examples:
Code | Operations |
---|---|
#[Get]
class User {}
|
|
#[GetCollection]
class User {}
|
|
#[Get("/users")]
class User {}
|
|
#[Post("/users")]
#[Patch("/users/{id}")]
class User {}
|
|
// See these as aliases to the `/users/{id}` Resource:
#[Get("/companies/{companyId}/users/{id}")]
#[Delete("/companies/{companyId}/users/{id}")]
class User {}
|
|
To link these operations with identifiers, refer to URI Variables decision record, for example:
<?php
use Company;
#[Get(
uriTemplate: "/companies/{companyId}/users/{id}",
uriVariables: [
"companyId" => ["class" => Company::class, "identifiers" => ["id"], "property" => "user"],
"id" => ["class" => User::class, "identifiers" => ["id"]]
]
)]
class User {
#[ApiProperty(identifier=true)]
public $id;
public Company $company;
}
The ApiResource
attribute could be used to set defaults properties on operations:
<?php
#[ApiResource(normalizationContext=["groups"= [....]])]
#[Get("/users/{id}")]
class User {}
These properties can also be specified directly on the verb attribute:
<?php
#[Get("/users/{id}", normalizationContext=["group"])]
class User {}
Internally, HTTP verbs are aliases to the Resource Attribute holding a method and a default path. The ApiResource
attribute is a reflection of the underlying metadata:
<?php
namespace ApiPlatform\Metadata;
class ApiResource {
public string $iri;
public bool $mercure;
public string $security;
public bool $paginationEnabled;
// etc.
/** this was named $attributes before **/
public array $extraProperties;
}
For GraphQL, Query
, Mutation
and Subscription
will be added.
An Operation
attribute was proposed, but we want to keep the code base small and decided verb attributes where sufficient.
The initially proposed CGet
will be named GetCollection
. It is only a shortcut to what used to be called collectionOperation
on the GET
verb. To remove confusion around collectionOperations
and itemOperations
, these terms will be deprecated in the code-base. To distant ourselves from the CRUD
pattern, we also declined List
and Create
as we want to focus on Resource based architectures. RPC
routes will be easy to add using the Post
verb if required.