v2.5 Subresources

Subresources

A subresource is a collection or an item that belongs to another resource. API Platform makes it easy to create such operations.

Subresources screencast
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:

```php namespace App\Entity;

use ApiPlatform\Core\Annotation\ApiResource; use Doctrine\ORM\Mapping as ORM;

/**

  • @ORM\Entity

  • @ApiResource / class Answer { /*

    • @ORM\Column(type=“integer”)
    • @ORM\Id
    • @ORM\GeneratedValue(strategy=“AUTO”) */ private $id;

    /**

    • @ORM\Column */ public $content;

    /**

    • @ORM\OneToOne(targetEntity=“Question”, mappedBy=“answer”) */ public $question;

    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 { /*

    • @ORM\Column(type=“integer”)
    • @ORM\Id
    • @ORM\GeneratedValue(strategy=“AUTO”) */ private $id;

    /**

    • @ORM\Column */ public $content;

    /**

    • @ORM\OneToOne(targetEntity=“Answer”, inversedBy=“question”)
    • @ORM\JoinColumn(referencedColumnName=“id”, unique=true)
    • @ApiSubresource */ public $answer;

    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

# Using Serialization Groups

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:

```php namespace App\Entity;

use ApiPlatform\Core\Annotation\ApiResource;

/**

  • @ApiResource(subresourceOperations={
  • "api_questions_answer_get_subresource"={
    
  •     "method"="GET",
    
  •     "normalization_context"={"groups"={"foobar"}}
    
  • }
    
  • }) */ class Answer { // … }

```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.

# Using Custom Paths

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
{
}

# Access Control of Subresources

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
 {
 }

# Limiting Depth

You can control depth of subresources with the parameter maxDepth. For example, if the Answer entity also has a subresource such as commentsand 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;

    // ...
}

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

Copyright © 2023 Kévin Dunglas

Sponsored by Les-Tilleuls.coop