State Providers

To retrieve data exposed by the API, API Platform uses classes called state providers. A state provider using Doctrine ORM to retrieve data from a database, a state provider using Doctrine MongoDB ODM to retrieve data from a document database, and a state provider using Elasticsearch-PHP to retrieve data from an Elasticsearch cluster are included with the library. The first one is enabled by default. These state providers natively support paged collections and filters. They can be used as-is and are perfectly suited to common uses.

API によって公開されたデータを取得するために、API プラットフォームは状態プロバイダーと呼ばれるクラスを使用します。ライブラリには、DoctrineORM を使用してデータベースからデータを取得する状態プロバイダー、Doctrine MongoDB ODM を使用してドキュメントデータベースからデータを取得する状態プロバイダー、Elasticsearch-PHP を使用して Elasticsearch クラスターからデータを取得する状態プロバイダーが含まれています。最初のものはデフォルトで有効になっています。これらのステート プロバイダーは、ページ コレクションとフィルターをネイティブにサポートします。そのまま使用でき、一般的な用途に最適です。

However, you sometimes want to retrieve data from other sources such as another persistence layer or a webservice. Custom state providers can be used to do so. A project can include as many state providers as needed. The first able to retrieve data for a given resource will be used.

ただし、別の永続化レイヤーや Web サービスなど、他のソースからデータを取得したい場合もあります。これには、カスタム状態プロバイダーを使用できます。プロジェクトには、必要な数の状態プロバイダーを含めることができます。特定のリソースのデータを取得できる最初のリソースが使用されます。

To do so you need to implement the ApiPlatform\State\ProviderInterface.

そのためには、ApiPlatform\State\ProviderInterface を実装する必要があります。

In the following examples we will create custom state providers for an entity class called App\Entity\BlogPost. Note, that if your entity is not Doctrine-related, you need to flag the identifier property by using #[ApiProperty(identifier: true) for things to work properly (see also Entity Identifier Case).

次の例では、App\Entity\BlogPost という名前のエンティティ クラスのカスタム状態プロバイダーを作成します。エンティティが Doctrine 関連でない場合は、#[ApiProperty(identifier: true) を使用して識別子プロパティにフラグを立てる必要があることに注意してください。適切に動作するようにします (エンティティ識別子のケースも参照してください)。

Creating a Custom State Provider

If the Symfony MakerBundle is installed in your project, you can use the following command to generate a custom state provider easily:

Symfony MakerBundle がプロジェクトにインストールされている場合、次のコマンドを使用してカスタム状態プロバイダーを簡単に生成できます。

bin/console make:state-provider

Let's start with a State Provider for the URI: /blog_posts/{id}.

URI の状態プロバイダーから始めましょう: /blog_posts/{id}。

First, your BlogPostProvider has to implement the ProviderInterface:

まず、BlogPostProvider は ProviderInterface を実装する必要があります。

<?php

namespace App\State;

use App\Entity\BlogPost;
use ApiPlatform\Metadata\Operation;
use ApiPlatform\State\ProviderInterface;

final class BlogPostProvider implements ProviderInterface
{
    /**
     * {@inheritDoc}
     */
    public function provide(Operation $operation, array $uriVariables = [], array $context = [])
    {
        return new BlogPost($uriVariables['id']);
    }
}

As this operation expects a BlogPost we return an instance of the BlogPost in the provide method. The uriVariables parameter is an array with the values of the URI variables.

この操作は BlogPost を想定しているため、提供メソッドで BlogPost のインスタンスを返します。uriVariables パラメータは、URI 変数の値を含む配列です。

To use this provider we need to configure the provider on the operation:

このプロバイダーを使用するには、操作でプロバイダーを構成する必要があります。

<?php

namespace App\Entity;

use ApiPlatform\Metadata\Get;
use App\State\BlogPostProvider;

#[Get(provider: BlogPostProvider::class)]
class BlogPost {}

If you use the default configuration, the corresponding service will be automatically registered thanks to autowiring. To declare the service explicitly, you can use the following snippet:

デフォルト設定を使用する場合、autowiring により、対応するサービスが自動的に登録されます。サービスを明示的に宣言するには、次のスニペットを使用できます。

# api/config/services.yaml
services:
    # ...
    App\State\BlogPostProvider: ~
        # Uncomment only if autoconfiguration is disabled
        #tags: [ 'api_platform.state_provider' ]

Now let's say that we also want to handle the /blog_posts URI which returns a collection. We can change the Provider into supporting a wider range of operations. Then we can provide a collection of blog posts when the operation is a CollectionOperationInterface:

ここで、コレクションを返す /blog_posts URI も処理したいとしましょう。プロバイダーを変更して、より幅広い操作をサポートできます。次に、操作が CollectionOperationInterface の場合、ブログ投稿のコレクションを提供できます。

<?php

namespace App\State;

use App\Entity\BlogPost;
use ApiPlatform\Metadata\Operation;
use ApiPlatform\State\ProviderInterface;
use ApiPlatform\Metadata\CollectionOperationInterface;

final class BlogPostProvider implements ProviderInterface
{
    /**
     * {@inheritDoc}
     */
    public function provide(Operation $operation, array $uriVariables = [], array $context = [])
    {
        if ($operation instanceof CollectionOperationInterface) {
            return [new BlogPost(), new BlogPost()];
        }

        return new BlogPost($uriVariables['id']);
    }
}

We then need to configure this same provider on the BlogPost GetCollection operation, or for every operations via the ApiResource attribute:

次に、BlogPost の GetCollection 操作で、または ApiResource 属性を介してすべての操作に対して、この同じプロバイダーを構成する必要があります。

<?php

namespace App\Entity;

use ApiPlatform\Metadata\ApiResource;
use App\State\BlogPostProvider;

#[ApiResource(provider: BlogPostProvider::class)]
class BlogPost {}

Hooking into the Built-In State Provider

If you want to execute custom business logic before or after retrieving data, this can be achieved by decorating the built-in state providers or using composition.

データを取得する前または後にカスタム ビジネス ロジックを実行する場合は、組み込みの状態プロバイダーを装飾するか、コンポジションを使用することで実現できます。

The next example uses a DTO to change the presentation for data originally retrieved by the default state provider.

次の例では、DTO を使用して、既定の状態プロバイダーによって最初に取得されたデータの表示を変更します。

<?php

namespace App\State;

use App\Dto\AnotherRepresentation;
use App\Model\Book;
use ApiPlatform\Metadata\Operation;
use ApiPlatform\State\ProviderInterface;

final class BookRepresentationProvider implements ProviderInterface
{
    public function __construct(private ProviderInterface $itemProvider)
    {
    }

    public function provide(Operation $operation, array $uriVariables = [], array $context = []): object|array|null
    {
        $book = $this->itemProvider->provide($operation, $uriVariables, $context);

        return new AnotherRepresentation(
            // Add DTO constructor params here.
            // $book->getTitle(),
        );
    }
}

Even with service autowiring and autoconfiguration enabled, you must still configure the decoration:

サービスの自動配線と自動構成が有効になっている場合でも、装飾を構成する必要があります。

# api/config/services.yaml
services:
    # ...
    App\State\BookRepresentationProvider:
        bind:
            $itemProvider: '@api_platform.doctrine.orm.state.item_provider'
        # Uncomment only if autoconfiguration is disabled
        #tags: [ 'api_platform.state_provider' ]

And configure that you want to use this provider on the Book resource:

そして、Book リソースでこのプロバイダーを使用するように構成します。

<?php

namespace App\Entity;

use ApiPlatform\Metadata\Get;
use App\Dto\AnotherRepresentation;
use App\State\BookRepresentationProvider;

#[Get(output: AnotherRepresentation::class, provider: BookRepresentationProvider::class)]
class Book {}