# State Processors To mutate the application states during `POST`, `PUT`, `PATCH` or `DELETE` [operations](operations.md), 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](serialization.md). With the Symfony variant, a state processor using [Doctrine ORM](https://www.doctrine-project.org/projects/orm.html) 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](https://www.doctrine-project.org/projects/doctrine-orm/en/current/reference/basic-mapping.html). A [Doctrine MongoDB ODM](https://www.doctrine-project.org/projects/mongodb-odm.html) state processor is also included and can be enabled by following the [MongoDB documentation](mongodb.md). With the Laravel variant, a state processor using [Eloquent ORM](https://laravel.com/docs/eloquent) is included with the library and is enabled by default. It is able to persist and delete objects that are also mapped as [Related Models](https://laravel.com/docs/eloquent-relationships#inserting-and-updating-related-models). However, you may want to: - store data to other persistence layers (Elasticsearch, external web services...) - not publicly expose the internal model mapped with the database through the API - use a separate model for [read operations](state-providers.md) and for updates by implementing patterns such as [CQRS](https://martinfowler.com/bliki/CQRS.html) 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 ### Custom State Processor with Symfony If the [Symfony MakerBundle](https://symfony.com/doc/current/bundles/SymfonyMakerBundle) is installed in your project, you can use the following command to generate a custom state processor easily: ```console bin/console make:state-processor ``` To create a state processor, you have to implement the [`ProcessorInterface`](https://github.com/api-platform/core/blob/main/src/State/ProcessorInterface.php). This interface defines a method `process`: to create, delete, update, or alter the given data in any ways. Here is an implementation example: ```php */ final class BlogPostProcessor implements ProcessorInterface { /** * @return BlogPost|void */ public function process(mixed $data, Operation $operation, array $uriVariables = [], array $context = []): mixed { // call your persistence layer to save $data return $data; } } ``` The `process()` method must return the created or modified object, or nothing (that's why `void` is allowed) for `DELETE` operations. The `process()` method can also take an object as input, in the `$data` parameter, that isn't of the same type that its output (the returned object). See [the DTO documentation entry](dto.md) for more details. We then configure our operation to use this processor: ```php */ final class BlogPostProcessor implements ProcessorInterface { /** * @return BlogPost|void */ public function process(mixed $data, Operation $operation, array $uriVariables = [], array $context = []): mixed { // call your persistence layer to save $data return $data; } } ``` The `process()` method must return the created or modified object, or nothing (that's why `void` is allowed) for `DELETE` operations. The `process()` method can also take an object as input, in the `$data` parameter, that isn't of the same type that its output (the returned object). See [the DTO documentation entry](dto.md) for more details. We then configure our operation to use this processor: ```php */ final class UserProcessor implements ProcessorInterface { public function __construct( #[Autowire(service: 'api_platform.doctrine.orm.state.persist_processor')] private ProcessorInterface $persistProcessor, #[Autowire(service: 'api_platform.doctrine.orm.state.remove_processor')] private ProcessorInterface $removeProcessor, private MailerInterface $mailer, ) { } /** * @return User|void */ public function process(mixed $data, Operation $operation, array $uriVariables = [], array $context = []): mixed { 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): void { // Your welcome email logic... // $this->mailer->send(...); } } ``` The `Autowire` attribute is used to inject the built-in processor services registered by API Platform. If you're using Doctrine MongoDB ODM instead of Doctrine ORM, replace `orm` by `odm` in the name of the injected services. Finally, configure that you want to use this processor on the User resource: ```php */ final class UserProcessor implements ProcessorInterface { public function __construct( private ProcessorInterface $persistProcessor, private ProcessorInterface $removeProcessor, ) { } /** * @return User|void */ public function process(mixed $data, Operation $operation, array $uriVariables = [], array $context = []): mixed { 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): void { // Your welcome email logic... // Mail::to($user->getEmail())->send(new WelcomeMail($user)); } } ``` Next, we bind the [PersistProcessor](https://github.com/api-platform/core/blob/main/src/Laravel/Eloquent/State/PersistProcessor.php) and [RemoveProcessor](https://github.com/api-platform/core/blob/main/src/Laravel/Eloquent/State/RemoveProcessor.php) in our Service Provider: ```php app->tag(UserProcessor::class, ProcessorInterface::class); $this->app->singleton(UserProcessor::class, function (Application $app) { return new UserProcessor( $app->make(PersistProcessor::class), $app->make(RemoveProcessor::class), ); }); } // ... } ``` Finally, configure that you want to use this processor on the User resource: ```php