A subresource is a collection or an item that belongs to another resource. API Platform makes it easy to create such operations.
Watch the Subresources screencast
The starting point of a subresource must be a relation on an existing resource.
For example, let’s create two entities (Question, Answer) and set up a subresource so that /question/42/answer
gives us
the answer to the question 42:
use ApiPlatform\Core\Annotation\ApiResource; use Doctrine\ORM\Mapping as ORM;
/**
@ORM\Entity
@ApiResource / class Answer { /*
/**
/**
public function getId(): ?int { return $this->id; }
// … }
// api/src/Entity/Question.php
namespace App\Entity;
use ApiPlatform\Core\Annotation\ApiResource; use ApiPlatform\Core\Annotation\ApiSubresource; use Doctrine\ORM\Mapping as ORM;
/**
@ORM\Entity
@ApiResource / class Question { /*
/**
/**
public function getId(): ?int { return $this->id; }
// … }
```yaml
# api/config/api_platform/resources.yaml
App\Entity\Answer: ~
App\Entity\Question:
properties:
answer:
subresource:
resourceClass: 'App\Entity\Answer'
collection: false
Note that all we had to do is to set up @ApiSubresource
on the Question::answer
relation. Because the answer
is a to-one relation, we know that this subresource is an item. Therefore the response will look like this:
{
"@context": "/contexts/Answer",
"@id": "/answers/42",
"@type": "Answer",
"id": 42,
"content": "Life, the Universe, and Everything",
"question": "/questions/42"
}
If you put the subresource on a relation that is to-many, you will retrieve a collection.
Last but not least, subresources can be nested, such that /questions/42/answer/comments
will get the collection of comments for the answer to question 42.
Note: only for GET
operations are supported at the moment
You may want custom groups on subresources, you can set normalization_context
or denormalization_context
on that operation. To do so, add a subresourceOperations
node. For example:
use ApiPlatform\Core\Annotation\ApiResource;
/**
"api_questions_answer_get_subresource"={
"method"="GET",
"normalization_context"={"groups"={"foobar"}}
}
```yaml
# api/config/api_platform/resources.yaml
App\Entity\Answer:
subresourceOperations:
api_questions_answer_get_subresource:
method: 'GET'
normalization_context: {groups: ['foobar']}
<?xml version="1.0" encoding="UTF-8" ?>
<!-- api/config/api_platform/resources.xml -->
<resources xmlns="https://api-platform.com/schema/metadata"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="https://api-platform.com/schema/metadata
https://api-platform.com/schema/metadata/metadata-2.0.xsd">
<resource class="App\Entity\Answer">
<subresourceOperations>
<subresourceOperation name="api_questions_answer_get_subresource">
<attribute name="method">GET</attribute>
<attribute name="normalization_context">
<attribute name="groups">
<attribute>foobar</attribute>
</attribute>
</attribute>
</subresourceOperation>
</subresourceOperations>
</resource>
</resources>
In the previous examples, the method
attribute is mandatory, because the operation name doesn’t match a supported HTTP
method.
Note that the operation name, here api_questions_answer_get_subresource
, is the important keyword.
It’ll be automatically set to $resources_$subresource(s)_get_subresource
. To find the correct operation name you
may use bin/console debug:router
.
You can control the path of subresources with the path
option of the subresourceOperations
parameter:
<?php
// api/src/Entity/Question.php
/**
* ...
* @ApiResource(
* subresourceOperations={
* "api_questions_answer_get_subresource"={
* "method"="GET",
* "path"="/questions/{id}/all-answers"
* },
* },
* )
*/
class Question
{
}
The subresourceOperations
attribute also allows you to add an access control on each path with the attribute security
.
<?php
// api/src/Entity/Answer.php
/**
* ...
* @ApiResource(
* subresourceOperations={
* "api_questions_answer_get_subresource"= {
* "security"="has_role('ROLE_AUTHENTICATED')"
* }
* }
* )
*/
class Answer
{
}
You can control depth of subresources with the parameter maxDepth
. For example, if the Answer
entity also has a subresource
such as comments
and you don’t want the route api/questions/{id}/answers/{id}/comments
to be generated. You can do this by adding the parameter maxDepth in the ApiSubresource annotation or YAML/XML file configuration.
<?php
// api/src/Entity/Question.php
use ApiPlatform\Core\Annotation\ApiProperty;
use ApiPlatform\Core\Annotation\ApiResource;
use ApiPlatform\Core\Annotation\ApiSubresource;
/**
* ...
* @ApiResource
*/
class Question
{
/**
* ...
* @ApiSubresource(maxDepth=1)
*/
public $answer;
// ...
}
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