
Symfony:api Platform State Providers Skill
- 431 installs
- 190 repo stars
- Updated August 6, 2026
- makfly/superpowers-symfony
symfony:api-platform-state-providers is a Claude Code skill that customizes API Platform read/write pipelines with StateProvider and StateProcessor classes for non-trivial data sourcing and persistence in Symfony apps.
About
symfony:api-platform-state-providers is a Claude Code skill from makfly/superpowers-symfony that guides implementation of custom API Platform StateProvider and StateProcessor classes in Symfony. Developers use it when default Doctrine persistence is insufficient—aggregated reads, multi-source hydration, transactional writes, or side-effect hooks on create/update/delete. The skill covers provider/processor registration, DTO mapping, and pipeline ordering consistent with API Platform 3.x conventions. Reach for it while building REST or GraphQL resources that need bespoke data access without bypassing the API Platform state layer.
- Implement custom StateProvider for complex reads
- Build StateProcessor logic for writes and side effects
- Decorate default providers without breaking metadata
- Wire DTOs, entities, and external sources cleanly
- Keep OpenAPI operations aligned with custom state
Symfony:Api Platform State Providers by the numbers
- 431 all-time installs (skills.sh)
- Ranked #1,030 of 4,492 Backend & APIs skills by installs in the Skillselion catalog
- Data as of Aug 11, 2026 (Skillselion catalog sync)
npx skills add https://github.com/makfly/superpowers-symfony --skill symfonyapi-platform-state-providersAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 431 |
|---|---|
| repo stars | ★ 190 |
| Last updated | August 6, 2026 |
| Repository | makfly/superpowers-symfony ↗ |
How do you customize API Platform state providers?
Customize API Platform read/write pipelines with custom StateProvider and StateProcessor classes for non-trivial data sourcing and persistence.
Who is it for?
Symfony developers building API Platform resources that need custom read aggregation, multi-source hydration, or non-Doctrine write side effects.
Skip if: Plain Symfony controllers without API Platform, front-end GraphQL clients, or CRUD resources fully satisfied by default Doctrine providers.
When should I use this skill?
A developer needs custom API Platform StateProvider or StateProcessor classes for non-trivial read sourcing or write persistence in a Symfony project.
What you get
StateProvider and StateProcessor PHP classes, service tags, and wired read/write pipeline for API Platform resources
- StateProvider PHP class
- StateProcessor PHP class
- Registered API Platform read/write pipeline
Files
Api Platform State Providers (Symfony)
Use when
- Designing or evolving API Platform contracts and operations.
- Aligning serialization, validation, and security behavior.
Default workflow
1. Define operation-level contract and payload boundaries. 2. Implement resource/DTO/provider/processor changes with explicit mapping. 3. Apply operation-specific validation and security constraints. 4. Validate functional behavior across happy and negative paths.
Guardrails
- Keep API contract explicit and version-aware.
- Avoid exposing internal entity fields implicitly.
- Prevent drift between docs and actual serialization.
Progressive disclosure
- Use this file for execution posture and risk controls.
- Open references when deep implementation details are needed.
Output contract
- API artifacts changed (resource/DTO/provider/processor).
- Contract/security decisions and rationale.
- Functional verification results.
References
reference.mddocs/complexity-tiers.md
API Platform State Providers & Processors Reference (Symfony)
Targets API Platform v4 (current 4.3). v3.4 deltas flagged inline. The provider/processor architecture replaced v2's DataProvider/DataPersister in v3.0.
ProviderInterface (reads)
Namespace ApiPlatform\State. v4 narrowed the return type to iterable|object|null; v3.4 declared it as mixed (code returning iterable/object/null is forward-compatible).
<?php
// src/State/BookProvider.php
namespace App\State;
use ApiPlatform\Metadata\CollectionOperationInterface;
use ApiPlatform\Metadata\Operation;
use ApiPlatform\State\ProviderInterface;
/**
* @implements ProviderInterface<Book>
*/
final class BookProvider implements ProviderInterface
{
public function provide(
Operation $operation,
array $uriVariables = [],
array $context = []
): iterable|object|null { // v4 signature — v3.4 returns `mixed`
if ($operation instanceof CollectionOperationInterface) {
return $this->repository->findAllActive();
}
return $this->repository->find($uriVariables['id']); // null → 404
}
}Wire it on the resource:
#[ApiResource(provider: BookProvider::class)]
#[Get(provider: BookProvider::class)]Autowiring auto-registers providers. Without autowiring, tag with api_platform.state_provider.
Decorating the built-in Doctrine provider
Wrap the default provider (composition) to keep Doctrine fetching but post-process the result — e.g. return a DTO or enforce a tenant scope:
<?php
// src/State/BookProvider.php
namespace App\State;
use ApiPlatform\Metadata\Operation;
use ApiPlatform\State\ProviderInterface;
use Symfony\Component\DependencyInjection\Attribute\Autowire;
final class BookProvider implements ProviderInterface
{
public function __construct(
#[Autowire(service: 'api_platform.doctrine.orm.state.collection_provider')]
private ProviderInterface $collectionProvider,
#[Autowire(service: 'api_platform.doctrine.orm.state.item_provider')]
private ProviderInterface $itemProvider,
) {}
public function provide(Operation $operation, array $uriVariables = [], array $context = []): iterable|object|null
{
$provider = $operation instanceof \ApiPlatform\Metadata\CollectionOperationInterface
? $this->collectionProvider
: $this->itemProvider;
$data = $provider->provide($operation, $uriVariables, $context);
// business logic / transformation here
return $data;
}
}Built-in service IDs:
- ORM item provider:
api_platform.doctrine.orm.state.item_provider - ORM collection provider:
api_platform.doctrine.orm.state.collection_provider
ProcessorInterface (writes)
Namespace ApiPlatform\State. Signature unchanged since v3:
<?php
// src/State/BookProcessor.php
namespace App\State;
use ApiPlatform\Metadata\Operation;
use ApiPlatform\State\ProcessorInterface;
use Symfony\Component\DependencyInjection\Attribute\Autowire;
final class BookProcessor implements ProcessorInterface
{
public function __construct(
#[Autowire(service: 'api_platform.doctrine.orm.state.persist_processor')]
private ProcessorInterface $persistProcessor,
) {}
public function process(
mixed $data,
Operation $operation,
array $uriVariables = [],
array $context = []
): mixed {
// pre-persist business logic (hashing, slug, audit…)
$result = $this->persistProcessor->process($data, $operation, $uriVariables, $context);
// post-persist side effects (dispatch message, send mail…)
return $result;
}
}Returns the created/modified object, or void for DELETE.
Built-in processor service IDs (Symfony / Doctrine ORM):
api_platform.doctrine.orm.state.persist_processorapi_platform.doctrine.orm.state.remove_processor
Wire on operations:
#[Post(processor: BookProcessor::class)]
#[Delete(processor: 'api_platform.doctrine.orm.state.remove_processor')]Without autowiring, tag with api_platform.state_processor.
NEW in v4 — write: true on read operations (CQRS)
v4 lets a processor run on safe HTTP methods (GET / GetCollection), so a "read" endpoint can dispatch a query/command handler:
#[GetCollection(processor: CarReportProcessor::class, write: true)]
class Car {}# YAML equivalent
resources:
App\Entity\Car:
operations:
ApiPlatform\Metadata\GetCollection:
processor: App\State\CarReportProcessor
write: trueThis is the notable v4 capability — in v3.4 processors only ran on write methods.
Skill Operating Checklist
Design checklist
- Confirm operation boundaries and invariants first.
- Minimize scope while preserving contract correctness.
- Test both happy path and negative path behavior.
Validation commands
- ./vendor/bin/phpunit --filter=Api
- ./vendor/bin/phpstan analyse
- php bin/console debug:router
Failure modes to test
- Invalid payload or forbidden actor.
- Boundary values / not-found cases.
- Retry or partial-failure behavior for async flows.
Related skills
FAQ
When do Symfony developers need custom StateProviders?
symfony:api-platform-state-providers applies when default Doctrine providers cannot serve aggregated reads, multi-source hydration, or computed fields. Custom StateProvider classes plug into API Platform's read pipeline without abandoning the state abstraction.
What does a StateProcessor handle in API Platform?
A StateProcessor in API Platform executes write-side persistence and side effects on create, update, or delete. symfony:api-platform-state-providers guides Symfony service registration and transactional write logic beyond standard Doctrine flush behavior.