Skip to content

Commit c984ab6

Browse files
authored
Merge pull request #3838 from soyuka/adr
Subresource definition ADR proposal
2 parents 9663fda + a009340 commit c984ab6

File tree

1 file changed

+157
-0
lines changed

1 file changed

+157
-0
lines changed
Lines changed: 157 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,157 @@
1+
# Subresource Definition
2+
3+
* Status: proposed
4+
* Deciders: @dunglas, @vincentchalamon, @soyuka, @GregoireHebert, @Deuchnord
5+
6+
## Context and Problem Statement
7+
8+
Subresources introduced in 2017 ([#904][pull/904]) the `ApiSubresource` annotation. This definition came along with its own set of issues ([#2706][issue/2706]) and needs a refreshment. On top of that, write support on subresources is a wanted feature and it is hard to implement currently ([#2598][pull/2598]) (See [ADR-0001-subresource-write-support](./0001-subresource-write-support.md)). How can we revamp the Subresource definition to improve the developer experience and reduce the complexity?
9+
10+
## Considered Options
11+
12+
* Fix the current `ApiSubresource` annotation
13+
* Use multiple `ApiResource` to declare subresources and deprecate `ApiSubresource`
14+
* Deprecate subresources
15+
16+
## Decision Outcome
17+
18+
We choose to use multiple `ApiResource` annotations to declare subresources on a given Model class:
19+
20+
* Subresource declaration is an important feature and removing it would harm the software.
21+
* The `ApiSubresource` annotation is declared on a Model's properties, which was identified as the root of several issues. For example, finding what class it is defined on ([#3458][issue/3458]). Having multiple `ApiResource` would improve a lot the declaration of our internal metadata and would cause less confusion for developers.
22+
* The `path` of these multiple `ApiResource` needs to be explicitly described.
23+
* An `ApiResource` is always defined on the Resource it represents: `/companies/1/users` outputs Users and should be defined on the `User` model.
24+
* PropertyInfo and Doctrine metadata can be used to define how is the Resource identified according to the given path.
25+
26+
### Examples
27+
28+
Get Users belonging to the company on (`/companies/1/users`);
29+
30+
```php
31+
/**
32+
* @ApiResource(path="/users")
33+
* @ApiResource(path="/companies/{companyId}/users")
34+
*/
35+
class User {
36+
/** @ApiProperty(identifier=true) */
37+
public int $id;
38+
39+
/** @var Company[] */
40+
public array $companies = [];
41+
}
42+
```
43+
44+
With explicit identifiers, the tuple is explained in [ADR-0002-identifiers](./0002-identifiers) `{parameterName: {Class, property}}`:
45+
46+
```php
47+
/**
48+
* @ApiResource(path="/users", identifiers={"id": {User::class, "id"}})
49+
* @ApiResource(path="/companies/{companyId}/users", identifiers={"companyId": {Company::class, "id"}, "id": {User::class, "id"}})
50+
*/
51+
class User {
52+
/** @ApiProperty(identifier=true) */
53+
public int $id;
54+
55+
/** @var Company[] */
56+
public array $companies = [];
57+
}
58+
```
59+
60+
Two-level subresource to get the Users belonging to the Company #1 located in France `/countries/fr/companies/1/users`:
61+
62+
```php
63+
/**
64+
* @ApiResource(path="/users")
65+
* @ApiResource(path="/countries/{countryId}/companies/{companyId}/users")
66+
*/
67+
class User {
68+
/** @ApiProperty(identifier=true) */
69+
public int $id;
70+
71+
/** @var Company[] */
72+
public array $companies = [];
73+
}
74+
75+
class Company {
76+
/** @ApiProperty(identifier=true) */
77+
public int $id;
78+
79+
/** @var Country[] **/
80+
public array $countries = [];
81+
}
82+
83+
class Country {
84+
/** @ApiProperty(identifier=true) */
85+
public string $shortName;
86+
}
87+
```
88+
89+
With explicit identifiers:
90+
91+
```php
92+
/**
93+
* @ApiResource(path="/users", identifiers={"id": {User::class, "id"}})
94+
* @ApiResource(path="/countries/{countryId}/companies/{companyId}/users", identifiers={"companyId": {Company::class, "id"}, "countryId": {Country::class, "shortName"}, "id": {User::class, "id"}})
95+
*/
96+
class User {
97+
/** @ApiProperty(identifier=true) */
98+
public int $id;
99+
100+
/** @var Company[] */
101+
public array $companies = [];
102+
}
103+
104+
class Company {
105+
/** @ApiProperty(identifier=true) */
106+
public int $id;
107+
108+
/** @var Country[] **/
109+
public array $countries = [];
110+
}
111+
112+
class Country {
113+
/** @ApiProperty(identifier=true) */
114+
public string $shortName;
115+
}
116+
```
117+
118+
Get the company employees or administrators `/companies/1/administrators`:
119+
120+
```php
121+
/**
122+
* @ApiResource(path="/users")
123+
* @ApiResource(path="/companies/{companyId}/administrators")
124+
* @ApiResource(path="/companies/{companyId}/employees")
125+
*/
126+
class User {
127+
/** @ApiProperty(identifier=true) */
128+
public int $id;
129+
130+
/** @var Company[] */
131+
public array $companies = [];
132+
}
133+
134+
class Company {
135+
/** @ApiProperty(identifier=true) */
136+
public int $id;
137+
138+
/** @var User[] **/
139+
public array $employees;
140+
141+
/** @var User[] **/
142+
public array $administrators;
143+
}
144+
```
145+
146+
This example will require a custom DataProvider as the discriminator needs to be explicit.
147+
148+
## Links
149+
150+
* [Subresource refactor][pull/3689]
151+
152+
153+
[pull/904]: https://github.com/api-platform/core/pull/904 "Subresource feature"
154+
[issue/2706]: https://github.com/api-platform/core/issues/2706 "Subresource RFC"
155+
[pull/2598]: https://github.com/api-platform/core/pull/2598 "Subresource write support"
156+
[issue/3458]: https://github.com/api-platform/core/pull/3458 "Subresource poor DX"
157+
[pull/3689]: https://github.com/api-platform/core/pull/3689 "Revamp subresource"

0 commit comments

Comments
 (0)