State management in Api Platform
Fetch data and mutate state for custom api resources when using the Api Platform framework.
Define an api resource⌗
The following defines an api resource for a product. Note the provider classes set for the operations annotation.
<?php
declare(strict_types=1);
namespace App\ApiResource;
use App\Entity\Product;
use ApiPlatform\Metadata\Get;
use App\State\ProductProvider;
use ApiPlatform\Metadata\ApiResource;
use ApiPlatform\Metadata\GetCollection;
#[ApiResource(
    shortName: 'Product',
    operations: [
        new Get(provider: ProductProvider::class),
        new GetCollection(provider: ProductProvider::class)
    ]
)]
final class ProductResource
{
    public string $id;
    public string $name;
    public int $price;
    public static function fromEntity(Product $entity): self
    {
        $productResource = new self();
        $productResource->id = (string) $entity->id();
        $productResource->name = $entity->name();
        $productResource->price = $entity->price();
        return $productResource;
    }
}
State Providers⌗
The following defines a state provider for the above resource.
<?php
declare(strict_types=1);
namespace App\State;
use ApiPlatform\Metadata\Operation;
use App\ApiResource\ProductResource;
use App\Repository\ProductRepository;
use ApiPlatform\State\ProviderInterface;
use ApiPlatform\Metadata\CollectionOperationInterface;
use Symfony\Component\Uid\Ulid;
final class ProductProvider implements ProviderInterface
{
    public function __construct(private readonly ProductRepository $productRepository)
    {
    }
    public function provide(Operation $operation, array $uriVariables = [], array $context = []): object|array|null
    {
        if ($operation instanceof CollectionOperationInterface) {
            $products = [];
            foreach ($this->productRepository->findAll() as $product) {
                $products[] = ProductResource::fromEntity($product);
            }
            return $products;
        }
        return ProductResource::fromEntity($this->productRepository->find(Ulid::fromString($uriVariables['id'])));
    }
}
State Processors⌗
Continuing with the above resource, in order to mutate its state a new operation should be configured. E.g to enable post request to create new product:
#[ApiResource(
    shortName: 'Product',
    operations: [
        new Get(provider: ProductProvider::class),
        new GetCollection(provider: ProductProvider::class),
        new Post(processor: ProductProcessor::class), // new operation defining processor
    ]
)]
final class ProductResource
And the corresponding processor:
// App\State\ProductProcessor.php
final class ProductProcessor implements ProcessorInterface
{
    public function process($data, Operation $operation, array $uriVariables = [], array $context = [])
    {
        // mutate and persist data as you like here, e.g:
        $this->productRepository->add(ProductResource::toEntity($data));
        return $data;
    }
}
Read the documentation for more info on state providers and state processors.
Read other posts