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.
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.
However, you may want to:
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.
If the Symfony MakerBundle is installed in your project, you can use the following command to generate a custom state processor easily:
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.
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/config/services.yaml
services:
# ...
App\State\BlogPostProcessor: ~
# Uncomment only if autoconfiguration is disabled
#tags: [ 'api_platform.state_processor' ]
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.
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:
<?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'
# If you're using Doctrine MongoDB ODM, you can use the following code:
# $persistProcessor: '@api_platform.doctrine_mongodb.odm.state.persist_processor'
# $removeProcessor: '@api_platform.doctrine_mongodb.odm.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 {}
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