Subresources

A Subresource is another way of declaring a resource that usually involves a more complex URI. In API Platform you can declare as many ApiResource as you want on a PHP class creating Subresources.

サブリソースは、通常はより複雑な URI を含むリソースを宣言する別の方法です。API プラットフォームでは、サブリソースを作成する PHP クラスで必要な数の ApiResource を宣言できます。

Subresources work very well by implementing your own state providers or processors. In API Platform we provide a working Doctrine layer for subresources providing you add the correct configuration for URI Variables.

サブリソースは、独自の状態プロバイダーまたはプロセッサーを実装することで非常にうまく機能します。 API プラットフォームでは、URI 変数の正しい構成を追加することで、サブリソース用の動作する Doctrine レイヤーを提供します。

URI Variables Configuration

URI Variables are configured via the uriVariables node on an ApiResource. It's an array indexed by the variables present in your URI, /companies/{companyId}/employees/{id} has two uri variables companyId and id. For each of these, we need to create a Link between the previous and the next node, in this example the link between a Company and an Employee.

URI 変数は、ApiResource の uriVariables ノードを介して構成されます。これは、URI に存在する変数によってインデックス付けされた配列です。/companies/{companyId}/employees/{id} には、companyId と id の 2 つの uri 変数があります。これらのそれぞれについて、前のノードと次のノードの間のリンクを作成する必要があります。この例では、会社と従業員の間のリンクです。

If you're using the Doctrine implementation, queries are automatically built using the provided links.

Doctrine 実装を使用している場合、クエリは提供されたリンクを使用して自動的に構築されます。

Answer to a Question

For this example we have two classes, a Question and an Answer. We want to find the Answer to the Question about the Universe using the following URI: /question/42/answer.

この例では、質問と回答の 2 つのクラスがあります。次の URI を使用して、宇宙に関する質問への回答を見つけたいと考えています: /question/42/answer。

Let's start by defining the resources:

リソースを定義することから始めましょう。

[codeSelector]

[コードセレクター]

<?php
// api/src/Entity/Answer.php
namespace App\Entity;

use ApiPlatform\Metadata\ApiResource;
use Doctrine\ORM\Mapping as ORM;

#[ORM\Entity]
#[ApiResource]
class Answer
{
    #[ORM\Id, ORM\Column, ORM\GeneratedValue]
    private ?int $id = null;

    #[ORM\Column(type: 'text')]
    public string $content;

    #[ORM\OneToOne]
    public Question $question;

    public function getId(): ?int
    {
        return $this->id;
    }

    // ...
}

// api/src/Entity/Question.php
namespace App\Entity;

use ApiPlatform\Metadata\ApiResource;
use Doctrine\ORM\Mapping as ORM;

#[ORM\Entity]
#[ApiResource]
class Question
{
    #[ORM\Id, ORM\Column, ORM\GeneratedValue]
    private ?int $id = null;

    #[ORM\Column(type: 'text')]
    public string $content;

    #[ORM\OneToOne]
    #[ORM\JoinColumn(referencedColumnName: 'id', unique: true)]
    public Answer $answer;

    public function getId(): ?int
    {
        return $this->id;
    }

    // ...
}
<?xml version="1.0" encoding="UTF-8" ?>
<!-- api/config/api_platform/resources.xml -->

<resources xmlns="https://api-platform.com/schema/metadata/resources-3.0"
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xsi:schemaLocation="https://api-platform.com/schema/metadata/resources-3.0
        https://api-platform.com/schema/metadata/resources-3.0.xsd">
    <resource class="App\Entity\Question"/>
    <resource class="App\Entity\Answer"/>

</resources>

[/codeSelector]

[/コードセレクター]

Now to create a new way of retrieving an Answer we will declare another resource on the Answer class. To make things work, API Platform needs informations about how to retrieve the Answer belonging to the Question, this is done by configuring the uriVariables:

ここで、回答を取得する新しい方法を作成するために、Answer クラスで別のリソースを宣言します。うまく機能させるために、API プラットフォームは、質問に属する回答を取得する方法に関する情報を必要とします。これは、uriVariables を構成することによって行われます。

[codeSelector]

[コードセレクター]

<?php
// api/src/Entity/Answer.php
namespace App\Entity;

use ApiPlatform\Metadata\ApiResource;
use ApiPlatform\Metadata\Link;
use Doctrine\ORM\Mapping as ORM;

#[ORM\Entity]
#[ApiResource]
#[ApiResource(
    uriTemplate: '/questions/{id}/answer', 
    uriVariables: [
        'id' => new Link(
            fromClass: Question::class,
            fromProperty: 'answer'
        )
    ], 
    operations: [new Get()]
)]
class Answer
{
    // ...
}
<resources xmlns="https://api-platform.com/schema/metadata/resources-3.0"
           xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
           xsi:schemaLocation="https://api-platform.com/schema/metadata/resources-3.0
        https://api-platform.com/schema/metadata/resources-3.0.xsd">

    <resource class="App\Entity\Question"/>

    <resource class="App\Entity\Answer"/>

    <resource class="App\Entity\Answer" uriTemplate="/questions/{id}/answer">
        <uriVariables>
            <uriVariable parameterName="id" fromClass="App\Entity\Question" fromProperty="answer"/>
        </uriVariables>

        <operations>
            <operation class="ApiPlatform\Metadata\Get"/>
        </operations>
    </resource>
</resources>    

[/codeSelector]

[/コードセレクター]

In this example, we instructed API Platform that the Answer we retrieve comes from the class Question from the property answer of that class.

この例では、API プラットフォームに、取得する Answer がクラス Question から取得され、そのクラスのプロパティ answer から取得されるように指示しました。

URI Variables are defined using Links (ApiPlatform\Metadata\Link). A Link can be binded either from or to a class and a property.

URI 変数は、リンク (ApiPlatform\Metadata\Link) を使用して定義されます。リンクは、クラスおよびプロパティから、またはクラスおよびプロパティにバインドできます。

If we had a relatedQuestions property on the Answer we could retrieve the collection of related questions via the following definition:

Answer に relatedQuestions プロパティがある場合、次の定義を介して関連する質問のコレクションを取得できます。

[codeSelector]

[コードセレクター]

#[ApiResource(
    uriTemplate: '/answers/{id}/related_questions.{_format}',
    uriVariables: [
        'id' => new Link(fromClass: Answer::class, fromProperty: 'relatedQuestions')
    ], 
    operations: [new GetCollection()]
)]
<resource class="App\Entity\Question" uriTemplate="/answers/{id}/related_questions.{_format}">
    <uriVariables>
        <uriVariable parameterName="id" fromClass="App\Entity\Answer" fromProperty="relatedQuestions"/>
    </uriVariables>

    <operations>
        <operation class="ApiPlatform\Metadata\GetCollection"/>
    </operations>

</resource>

[/codeSelector]

[/コードセレクター]

Company Employee's

Note that in this example, we declared an association using Doctrine only between Employee and Company using a ManyToOne. There is no inverse association hence the use of toProperty in the URI Variables definition.

この例では、ManyToOne を使用して Employee と Company の間でのみ Doctrine を使用して関連付けを宣言したことに注意してください。逆関連付けがないため、URI 変数定義で toProperty を使用します。

The following declares a few subresources: - /companies/{companyId}/employees/{id} - get an employee belonging to a company - /companies/{companyId}/employees - get the company employee's

以下は、いくつかのサブリソースを宣言します:- /companies/{companyId}/employees/{id} - 会社に属する従業員を取得します- /companies/{companyId}/employees - 会社の従業員の従業員を取得します

<?php
// api/src/Entity/Employee.php
namespace App\Entity;

use ApiPlatform\Metadata\ApiResource;
use ApiPlatform\Metadata\Link;
use ApiPlatform\Metadata\Get;
use ApiPlatform\Metadata\Post;
use ApiPlatform\Metadata\GetCollection;
use Doctrine\ORM\Mapping as ORM;

#[ORM\Entity]
#[ApiResource(
    operations: [ new Post() ]
)]
#[ApiResource(
    uriTemplate: '/companies/{companyId}/employees/{id}',
    uriVariables: [
        'companyId' => new Link(fromClass: Company::class, toProperty: 'company'),
        'id' => new Link(fromClass: Employee::class),
    ],
    operations: [ new Get() ]
)]
#[ApiResource(
    uriTemplate: '/companies/{companyId}/employees',
    uriVariables: [
        'companyId' => new Link(fromClass: Company::class, toProperty: 'company'),
    ],
    operations: [ new GetCollection() ]
)]
class Employee
{
    #[ORM\Id, ORM\Column, ORM\GeneratedValue]
    public ?int $id;

    #[ORM\Column]
    public string $name;

    #[ORM\ManyToOne(targetEntity: Company::class)]
    public ?Company $company;

    public function getId()
    {
        return $this->id;
    }
}

Now let's add the Company class:

それでは、Company クラスを追加しましょう。

<?php
// api/src/Entity/Employee.php
namespace App\Entity;

use ApiPlatform\Metadata\ApiResource;
use ApiPlatform\Metadata\Get;
use ApiPlatform\Metadata\Link;
use ApiPlatform\Metadata\Post;
use Doctrine\ORM\Mapping as ORM;

#[ORM\Entity]
#[ApiResource]
class Company
{
    #[ORM\Id, ORM\Column, ORM\GeneratedValue]
    public ?int $id;

    #[ORM\Column]
    public string $name;

    /** @var Employee[] */
    #[Link(toProperty: 'company')]
    public $employees = []; // only used to set metadata as GraphQl always needs to work from both sides of the association
}

We did not define any Doctrine annotation here and if we want thinks to work properly with GraphQL, we need to map the employees field as a Link to the class Employee using the property company.

ここでは Doctrine アノテーションを定義していません。考えを GraphQL で適切に動作させたい場合は、プロパティ company を使用して、Employees フィールドをクラス Employee へのリンクとしてマッピングする必要があります。

As a general rule, if the property we want to create a link from is in the fromClass, use fromProperty, if not, use toProperty.

原則として、リンクを作成するプロパティが fromClass にある場合は fromProperty を使用し、そうでない場合は toProperty を使用します。

For example, we could add a subresource fetching an employee's company. The company property belongs to the Employee class we can use fromProperty:

たとえば、従業員の会社を取得するサブリソースを追加できます。会社のプロパティは、fromProperty を使用できる Employee クラスに属します。

<?php 
#[ApiResource(
    uriTemplate: '/employees/{employeeId}/company',
    uriVariables: [
        'employeeId' => new Link(fromClass: Employee::class, fromProperty: 'company'),
    ],
    operations: [
        new Get()
    ]
)]

class Company {
    // ...
}