State providers and processors
Edit on GitHubAPI Platform splits resource handling into two components:
- Provider — returns data for read operations (
Get,GetCollection). - Processor — mutates data for write operations (
Post,Patch,Delete).
Spryker ships abstract base classes that handle operation dispatch, expose request/context helpers, and raise clear errors when a method is missing. Extend these instead of implementing ProviderInterface / ProcessorInterface directly.
Class hierarchy
>AbstractProvider AbstractProcessor
├── AbstractBackendProvider ├── AbstractBackendProcessor
└── AbstractStorefrontProvider └── AbstractStorefrontProcessor
- Use
AbstractBackend*for resources under theGlueBackendAPI (Zed admin context, user-scoped). - Use
AbstractStorefront*for resources under theGlue/GlueStorefrontAPI (customer-scoped). - Extend
AbstractProvider/AbstractProcessordirectly only when the resource is not tied to a backend or storefront context.
Providers
AbstractProvider dispatches operations based on the incoming Operation type:
| Operation | Method called |
|---|---|
Get |
provideItem() |
GetCollection |
provideCollection() |
Post |
returns null (the processor handles POST) |
Override only the methods you need. Unimplemented methods throw BadMethodCallException so missing handlers are caught early.
Example: storefront provider
<?php
declare(strict_types=1);
namespace Pyz\Glue\Customer\Api\Storefront\Provider;
use Generated\Api\Storefront\CustomersStorefrontResource;
use Pyz\Glue\Customer\Business\CustomerFacadeInterface;
use Spryker\ApiPlatform\State\Provider\AbstractStorefrontProvider;
class CustomerStorefrontProvider extends AbstractStorefrontProvider
{
public function __construct(
private readonly CustomerFacadeInterface $customerFacade,
) {
}
protected function provideItem(): ?CustomersStorefrontResource
{
$customerReference = $this->getUriVariable('customerReference');
$customerTransfer = $this->customerFacade->findCustomerByReference($customerReference);
if ($customerTransfer === null) {
return null;
}
return (new CustomersStorefrontResource())->fromArray($customerTransfer->toArray());
}
/**
* @return array<\Generated\Api\Storefront\CustomersStorefrontResource>
*/
protected function provideCollection(): array
{
$pagination = $this->getPagination();
$collection = $this->customerFacade->getCustomerCollection($pagination);
$resources = [];
foreach ($collection->getCustomers() as $customerTransfer) {
$resources[] = (new CustomersStorefrontResource())->fromArray($customerTransfer->toArray());
}
return $resources;
}
}
Processors
AbstractProcessor dispatches write operations to explicit methods:
| Operation | Method called |
|---|---|
Post |
processPost($data) |
Patch |
processPatch($data) |
Delete |
processDelete() |
Example: backend processor
<?php
declare(strict_types=1);
namespace Pyz\Glue\Customer\Api\Backend\Processor;
use Generated\Api\Backend\CustomersBackendResource;
use Generated\Shared\Transfer\CustomerTransfer;
use Pyz\Glue\Customer\Business\CustomerFacadeInterface;
use Spryker\ApiPlatform\State\Processor\AbstractBackendProcessor;
class CustomerBackendProcessor extends AbstractBackendProcessor
{
public function __construct(
private readonly CustomerFacadeInterface $customerFacade,
) {
}
protected function processPost(mixed $data): CustomersBackendResource
{
$customerTransfer = (new CustomerTransfer())->fromArray($data->toArray(), true);
$customerTransfer = $this->customerFacade->createCustomer($customerTransfer)->getCustomerTransferOrFail();
return (new CustomersBackendResource())->fromArray($customerTransfer->toArray());
}
protected function processPatch(mixed $data): CustomersBackendResource
{
$customerTransfer = (new CustomerTransfer())
->fromArray($data->toArray(), true)
->setCustomerReference($this->getUriVariable('customerReference'));
$customerTransfer = $this->customerFacade->updateCustomer($customerTransfer)->getCustomerTransferOrFail();
return (new CustomersBackendResource())->fromArray($customerTransfer->toArray());
}
protected function processDelete(): null
{
$this->customerFacade->deleteCustomerByReference($this->getUriVariable('customerReference'));
return null;
}
}
Context helpers
Inside your provider or processor you have access to the active Operation, URI variables, and request-scoped data through protected helpers.
Common helpers (available on all abstracts)
| Method | Description |
|---|---|
getOperation() |
Current API Platform Operation. |
getUriVariables() |
All URI variables of the current route. |
hasUriVariable($name) |
Check whether a URI variable is set. |
getUriVariable($name) |
Read a URI variable; throws ApiPlatformContextException if missing. |
hasRequest() / getRequest() |
Current Symfony Request from the context. |
hasLocale() / getLocale() |
LocaleTransfer resolved by AcceptLanguageLocaleSubscriber. |
hasStore() / getStore() |
StoreTransfer resolved from the request. |
getPagination() (provider only) |
PaginationTransfer built from the page and perPage query params (defaults: 1, 10). |
Always use the has*() guard before calling a get*() helper when the value may be absent — the get*() helper throws ApiPlatformContextException if the attribute is missing.
Backend helpers
AbstractBackendProvider / AbstractBackendProcessor add:
| Method | Description |
|---|---|
hasUser() / getUser() |
UserTransfer of the authenticated Zed user. |
Storefront helpers
AbstractStorefrontProvider / AbstractStorefrontProcessor add:
| Method | Description |
|---|---|
hasCustomer() / getCustomer() |
CustomerTransfer of the authenticated customer. |
isGuestCustomer() |
true when the customer is a guest. |
getCustomerReference() |
Shortcut to getCustomer()->getCustomerReferenceOrFail(). |
Pagination
getPagination() reads the standard query parameters and returns a fully populated PaginationTransfer. Pass it to your business layer and return the result as an array or a paginated collection:
protected function provideCollection(): array
{
$collection = $this->customerFacade->getCustomerCollection($this->getPagination());
return array_map(
fn ($customerTransfer) => (new CustomersBackendResource())->fromArray($customerTransfer->toArray()),
$collection->getCustomers()->getArrayCopy(),
);
}
Pagination response links (first, last, prev, next) are attached automatically by the API Platform integration.
When to bypass the abstract classes
The abstract classes cover standard JSON:API CRUD. Implement ProviderInterface / ProcessorInterface directly when you need:
- Custom operation types or non-CRUD dispatch logic.
- Batch-loading across relationships — implement
BatchLoadableProviderInterfaceinstead. - Full control over the
provide()/process()signature (for example, returningTraversablePaginator).
Related documentation
Thank you!
For submitting the form