API Platform State Providers, Query extensions and Resolvers

API Platform provides a bunch of interfaces to customize and extend your API to your own wishes. Some interfaces or the logic behind them might be quite confusing though. Let's clear some things up in here.

State Providers (or simply Providers)

State Providers, Data Providers or simply Providers, however you wish to call them are used to query data from various sources, e.g. using from the database using a Doctrine provider.

When you have resources that can't be directly queried because there is no Doctrine entity backing them choosing to implement a State Provider is a good choice.

To do this implement the interface \ApiPlatform\State\ProviderInterface and define the necessary methods that come with it. Make sure that in addition to that you set the provider argument to point to your Provider class like shown in the code below.

<?php
use ApiPlatform\Metadata\ApiResource;
use ApiPlatform\Metadata\GraphQl\QueryCollection;

#[ApiResource(
    graphQlOperations: [
        new QueryCollection(
            name: 'myQueryName', // becomes myQueryNameMyResource as the name of the resource gets appended to this name.
            provider: MyProvider::class,
        )
    ]
)]
class MyResource
{
}

Query extensions

When you simply want to filter on specific items when a query is performed against for example a collection of your entities (or some types of entities) you should use Query extensions.

API Platform herefor too provides two interfaces in case you want to extend a query done via the Doctrine QueryBuilder, the \ApiPlatform\Doctrine\Orm\Extension\QueryCollectionExtensionInterface and \ApiPlatform\Doctrine\Orm\Extension\QueryItemExtensionInterface. The former for changing queries done when fetching collections and the other when a single item is fetched.

A simple example is shown below.

<?php
use ApiPlatform\Doctrine\Orm\Extension\QueryCollectionExtensionInterface;
use ApiPlatform\Doctrine\Orm\Extension\QueryItemExtensionInterface;
use ApiPlatform\Doctrine\Orm\Util\QueryNameGeneratorInterface;
use ApiPlatform\Metadata\Operation;
use Doctrine\ORM\QueryBuilder;

final class UnexposeInternalExchangesExtension implements QueryCollectionExtensionInterface, QueryItemExtensionInterface
{
    public function applyToCollection(QueryBuilder $queryBuilder, QueryNameGeneratorInterface $queryNameGenerator, string $resourceClass, Operation $operation = null, array $context = []): void
    {
        if ($resourceClass !== MyEntity::class) {
            return;
        }

        $alias = $queryBuilder->getRootAliases()[0];
        $queryBuilder->andWhere($queryBuilder->expr()->notIn($alias.'.code', [
            'test1',
            'test2'
        ]));
    }

    public function applyToItem(QueryBuilder $queryBuilder, QueryNameGeneratorInterface $queryNameGenerator, string $resourceClass, array $identifiers, Operation $operation = null, array $context = []): void
    {
        if ($resourceClass !== MyEntity::class) {
            return;
        }

        $alias = $queryBuilder->getRootAliases()[0];
        $queryBuilder->andWhere($queryBuilder->expr()->notIn($alias.'.code', [
            'test1',
            'test2'
        ]));
    }
}

Resolvers

Resolvers are there to do things with an already fetched collection or item. Implement \ApiPlatform\GraphQl\Resolver\QueryCollectionResolverInterface when acting on a collection or \ApiPlatform\GraphQl\Resolver\QueryItemResolverInterface when acting on an item.