API Platform provides a way to create your own Doctrine Extensions, allowing you to modify collection and item queries for API resources.
The power here is that you can customize or create conditional query logic on the queries generated by the default controllers/operations API platform provides. There's no need to write a custom controller if you simply want to tweak the underlying query.
Use Case
Imagine an app with a Product
entity which has a boolean active
property. You want to restrict results to only active products for everyone except administrators.
<?php
// src/Entity/Product.php
namespace App\Entity;
use ApiPlatform\Core\Annotation\ApiResource;
/**
* @ApiResource
*/
class Product
{
//...
/**
* @var boolean
* @ORM\Column(type="boolean", nullable=false)
*/
private $active = true;
//...
}
The Solution
- Create the extension class
- Create a service for the class
Creating the Extension Class
- Create the directory:
src/Doctrine
- Add a new file:
ActiveProductExtension.php
- Copy the source below into the new file
The extension class implements QueryCollectionExtensionInterface
and QueryItemExtensionInterface
which forces us to create the applyToCollection()
and applyToItem()
methods.
If you were only concerned with results returned from a collection, you may only implement QueryCollectionExtensionInterface
.
In addition to the 2 methods required by the interface, there is also an addWhere()
method. It is called from both apply*
methods and is where the real work happens.
<?php
// src/Doctrine/ActiveProductExtension.php
// Adapted from the API Platform CurrentUserExtension example.
namespace App\Doctrine;
use ApiPlatform\Core\Bridge\Doctrine\Orm\Extension\QueryCollectionExtensionInterface;
use ApiPlatform\Core\Bridge\Doctrine\Orm\Extension\QueryItemExtensionInterface;
use ApiPlatform\Core\Bridge\Doctrine\Orm\Util\QueryNameGeneratorInterface;
use App\Entity\Product;
use App\Entity\User;
use Doctrine\ORM\QueryBuilder;
use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface;
use Symfony\Component\Security\Core\Authorization\AuthorizationCheckerInterface;
final class ActiveProductExtension implements QueryCollectionExtensionInterface, QueryItemExtensionInterface
{
private $tokenStorage;
private $authorizationChecker;
public function __construct(TokenStorageInterface $tokenStorage, AuthorizationCheckerInterface $checker)
{
$this->tokenStorage = $tokenStorage;
$this->authorizationChecker = $checker;
}
/**
* {@inheritdoc}
*/
public function applyToCollection(QueryBuilder $queryBuilder, QueryNameGeneratorInterface $queryNameGenerator, string $resourceClass, string $operationName = null)
{
$this->addWhere($queryBuilder, $resourceClass);
}
/**
* {@inheritdoc}
*/
public function applyToItem(QueryBuilder $queryBuilder, QueryNameGeneratorInterface $queryNameGenerator, string $resourceClass, array $identifiers, string $operationName = null, array $context = [])
{
$this->addWhere($queryBuilder, $resourceClass);
}
/**
*
* @param QueryBuilder $queryBuilder
* @param string $resourceClass
*/
private function addWhere(QueryBuilder $queryBuilder, string $resourceClass)
{
$user = $this->tokenStorage->getToken()->getUser();
// Add the where clause if we're operating on a Product resource, and the user is not an admin.
if ($user instanceof User && Product::class === $resourceClass && !$this->authorizationChecker->isGranted('ROLE_ADMIN')) {
$rootAlias = $queryBuilder->getRootAliases()[0];
$queryBuilder->andWhere(sprintf('%s.active = true', $rootAlias));
}
}
}
Creating the Service
With the extension class created, all that is left is to create the service.
Add the following to: config/services.yaml
.
This assumes you have autowire
enabled by default.
services:
'App\Doctrine\CurrentProductExtension':
tags:
- { name: api_platform.doctrine.orm.query_extension.collection, priority: 9 }
- { name: api_platform.doctrine.orm.query_extension.item }
Result
With the extension and service in place, /api/products
and /api/products/{id}
will only return records which have active
set to true
, unless the user has the role ROLE_ADMIN
.
For more information on Doctrine Extensions with API Platform, visit the docs.
- Log in to post comments