State Processors

To mutate the application states during POST, PUT, PATCH or DELETE operations, API Platform uses classes called state processors. State processors receive an instance of the class marked as an API resource (usually using the #[ApiResource] attribute). This instance contains data submitted by the client during the deserialization process.

POST、PUT、PATCH、または DELETE 操作中にアプリケーションの状態を変更するために、API プラットフォームは状態プロセッサと呼ばれるクラスを使用します。状態プロセッサは、API リソース (通常は #[ApiResource] 属性を使用) としてマークされたクラスのインスタンスを受け取ります。このインスタンスには、逆シリアル化プロセス中にクライアントから送信されたデータが含まれています。

A state processor using Doctrine ORM is included with the library and is enabled by default. It is able to persist and delete objects that are also mapped as Doctrine entities. A Doctrine MongoDB ODM state processor is also included and can be enabled by following the MongoDB documentation.

Doctrine ORM を使用するステート プロセッサはライブラリに含まれており、デフォルトで有効になっています。 Doctrine エンティティとしてもマップされているオブジェクトを永続化および削除できます。Doctrine MongoDB ODM ステート プロセッサも含まれており、MongoDB ドキュメントに従って有効にすることができます。

However, you may want to:

ただし、次のことが必要な場合があります。

  • store data to other persistence layers (Elasticsearch, external web services...)
    データを他の永続レイヤー (Elasticsearch、外部 Web サービスなど) に保存します。
  • not publicly expose the internal model mapped with the database through the API
    API を介してデータベースにマッピングされた内部モデルを公開しない
  • use a separate model for read operations and for updates by implementing patterns such as CQRS
    CQRS などのパターンを実装して、読み取り操作と更新に別のモデルを使用する

Custom state processors can be used to do so. A project can include as many state processors as needed. The first able to process the data for a given resource will be used.

そのために、カスタム状態プロセッサを使用できます。プロジェクトには、必要な数のステート プロセッサを含めることができます。特定のリソースのデータを最初に処理できるものが使用されます。

Creating a Custom State Processor

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

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

bin/console make:state-processor

To create a state processor, you have to implement the ProcessorInterface. This interface defines a method process: to create, delete, update, or alter the given data in any ways.

ステート プロセッサを作成するには、ProcessorInterface を実装する必要があります。このインターフェイスは、任意の方法で指定されたデータを作成、削除、更新、または変更するためのメソッド プロセスを定義します。

Here is an implementation example:

実装例を次に示します。

<?php

namespace App\State;

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

class BlogPostProcessor implements ProcessorInterface
{
    /**
     * {@inheritDoc}
     */
    public function process($data, Operation $operation, array $uriVariables = [], array $context = [])
    {
        // call your persistence layer to save $data
        return $data;
    }
}

We then configure our operation to use this processor:

次に、このプロセッサを使用するように操作を構成します。

<?php

namespace App\Entity;

use ApiPlatform\Metadata\Post;
use App\State\BlogPostProcessor;

#[Post(processor: BlogPostProcessor::class)]
class BlogPost {}

If service autowiring and autoconfiguration are enabled (they are by default), you are done!

サービスの自動配線と自動構成が有効になっている場合 (デフォルトで有効になっています)、完了です。

Otherwise, if you use a custom dependency injection configuration, you need to register the corresponding service and add the api_platform.state_processor tag.

それ以外の場合、カスタムの依存性注入構成を使用する場合は、対応するサービスを登録し、api_platform.state_processor タグを追加する必要があります。

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

Hooking into the Built-In State Processors

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

永続化の前または後にカスタム ビジネス ロジックを実行する場合は、組み込みの状態プロセッサをデコレートするか、コンポジションを使用することで実現できます。

The next example uses Symfony Mailer. Read its documentation if you want to use it.

次の例では、Symfony Mailer を使用しています。使用する場合は、そのドキュメントをお読みください。

Here is an implementation example which sends new users a welcome email after a REST POST or GraphQL create operation, in a project using the native Doctrine ORM state processor:

以下は、ネイティブの Doctrine ORM ステート プロセッサを使用するプロジェクトで、REST POST または GraphQL 作成操作の後に新しいユーザーにウェルカム メールを送信する実装例です。

<?php

namespace App\State;

use ApiPlatform\Metadata\DeleteOperationInterface;
use ApiPlatform\Metadata\Operation;
use ApiPlatform\State\ProcessorInterface;
use App\Entity\User;
use Symfony\Component\Mailer\MailerInterface;

final class UserProcessor implements ProcessorInterface
{
    public function __construct(private ProcessorInterface $persistProcessor, private ProcessorInterface $removeProcessor, MailerInterface $mailer)
    {
    }

    public function process($data, Operation $operation, array $uriVariables = [], array $context = [])
    {
        if ($operation instanceof DeleteOperationInterface) {
            return $this->removeProcessor->process($data, $operation, $uriVariables, $context);
        }

        $result = $this->persistProcessor->process($data, $operation, $uriVariables, $context);
        $this->sendWelcomeEmail($data);
        return $result;
    }

    private function sendWelcomeEmail(User $user)
    {
        // Your welcome email logic...
        // $this->mailer->send(...);
    }
}

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

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

# api/config/services.yaml
services:
    # ...
    App\State\UserProcessor:
        bind:
            $persistProcessor: '@api_platform.doctrine.orm.state.persist_processor'
            $removeProcessor: '@api_platform.doctrine.orm.state.remove_processor'
        # Uncomment only if autoconfiguration is disabled
        #arguments: ['@App\State\UserProcessor.inner']
        #tags: [ 'api_platform.state_processor' ]

And configure that you want to use this processor on the User resource:

そして、ユーザー リソースでこのプロセッサを使用するように構成します。

<?php

namespace App\Entity;

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

#[ApiResource(processor: UserProcessor::class)]
class User {}