State Processors in Detail
State Processors in Detail
~14 Min. Lesezeit Zuletzt aktualisiert am August 8, 2026
State processors are the WRITE counterpart to chapter 57 – INSTEAD of READING data, they process data AFTER validation, BEFORE the final response.
The ProcessorInterface
<?php
declare(strict_types=1);
namespace App\State;
use ApiPlatform\Metadata\Operation;
use ApiPlatform\State\ProcessorInterface;
final class ExampleProcessor implements ProcessorInterface
{
public function process(mixed $data, Operation $operation, array $uriVariables = [], array $context = []): mixed
{
// $data: the (already validated) object from the request body
// Return: the object the client sees in the response
return $data;
}
}$data is ALREADY the deserialized AND validated object (block 3) – a processor NEVER sees invalid data, validation RUNS BEFOREHAND, INDEPENDENT of the processor.
When to use a processor instead of Doctrine lifecycle callbacks
The Symfony course used Doctrine #[ORM\PrePersist] callbacks for SIMILAR purposes – in API Platform, state processors are the PREFERRED choice, since they ALSO work with non-Doctrine resources (chapter 60) and are MORE EXPLICITLY VISIBLE in the #[ApiResource] configuration.
Handling the DELETE case
public function process(mixed $data, Operation $operation, array $uriVariables = [], array $context = []): mixed
{
if ($operation instanceof \ApiPlatform\Metadata\Delete) {
// $data is the object being DELETED, BEFORE the delete
}
return $this->persistProcessor->process($data, $operation, $uriVariables, $context);
}Achtung: For Delete operations, process() MUST ALWAYS return null (there's NO object left after deletion) – the wrapped default processor handles this AUTOMATICALLY and CORRECTLY, CUSTOM processor implementations (without wrapping) must handle this THEMSELVES.
Chaining multiple processors
A processor can inject and CALL another processor – EXACTLY how UserPasswordHasherProcessor (chapter 48) wraps the DEFAULT Doctrine processor. Multiple CUSTOM processors can theoretically be NESTED; in PRACTICE, ONE processor per resource usually stays CLEARER.
Tipp: $context contains ADDITIONAL metadata (among other things previous_data on PUT/PATCH, the OLD state BEFORE the change) – useful for detecting in CUSTOM logic WHAT specifically changed.