Skip to content

Instantly share code, notes, and snippets.

@philippecarle
Created September 18, 2018 07:26
Show Gist options
  • Save philippecarle/d99d71b9be62a69b2eb1cf3690957006 to your computer and use it in GitHub Desktop.
Save philippecarle/d99d71b9be62a69b2eb1cf3690957006 to your computer and use it in GitHub Desktop.
Discriminator Filter for API Platform
<?php
declare(strict_types=1);
namespace AppBundle\Filter;
use ApiPlatform\Core\Bridge\Doctrine\Orm\Filter\AbstractContextAwareFilter;
use ApiPlatform\Core\Bridge\Doctrine\Orm\Util\QueryNameGeneratorInterface;
use ApiPlatform\Core\Metadata\Resource\Factory\ResourceMetadataFactoryInterface;
use Doctrine\Common\Persistence\ManagerRegistry;
use Doctrine\Common\Persistence\ObjectManager;
use Doctrine\ORM\Mapping\ClassMetadataInfo;
use Doctrine\ORM\QueryBuilder;
use Psr\Log\LoggerInterface;
class DiscriminatorFilter extends AbstractContextAwareFilter
{
const FILTER_RESOURCE_TYPE_PROPERTY = 'resourceType';
/**
* @var ResourceMetadataFactoryInterface
*/
private $resourceMetadataFactory;
public function __construct(
ManagerRegistry $managerRegistry,
ResourceMetadataFactoryInterface $resourceMetadataFactory,
LoggerInterface $logger = null,
array $properties = null
) {
parent::__construct($managerRegistry, null, $logger, $properties);
$this->resourceMetadataFactory = $resourceMetadataFactory;
}
/**
* {@inheritdoc}
*/
public function getDescription(string $resourceClass): array
{
return [
static::FILTER_RESOURCE_TYPE_PROPERTY => [
'property' => static::FILTER_RESOURCE_TYPE_PROPERTY,
'type' => 'string',
'required' => false,
],
];
}
/**
* {@inheritdoc}
*/
protected function filterProperty(string $property, $value, QueryBuilder $queryBuilder, QueryNameGeneratorInterface $queryNameGenerator, string $resourceClass, string $operationName = null, array $context = [])
{
if ($property !== static::FILTER_RESOURCE_TYPE_PROPERTY) {
return;
}
/** @var ObjectManager $manager */
$manager = $this->managerRegistry->getManagerForClass($resourceClass);
$classMetadataFactory = $manager->getMetadataFactory();
/** @var ClassMetadataInfo $classMetadata */
$classMetadata = $classMetadataFactory->getMetadataFor($resourceClass);
foreach ($classMetadata->discriminatorMap as $discriminator => $entityClass) {
$metadata = $this->resourceMetadataFactory->create($entityClass);
if ($metadata->getShortName() === $value) {
$alias = $queryBuilder->getAllAliases()[0];
$queryBuilder->andWhere($alias.' INSTANCE OF :instance');
$queryBuilder->setParameter('instance', $discriminator);
return;
}
}
$queryBuilder->where('1=0');
}
}
@akira28
Copy link

akira28 commented Jun 23, 2020

I've made some little modifications to make it compatible with the latest versions of ApiPlatform:

<?php

declare(strict_types=1);

namespace App\Filter;

use ApiPlatform\Core\Bridge\Doctrine\Orm\Filter\AbstractContextAwareFilter;
use ApiPlatform\Core\Bridge\Doctrine\Orm\Util\QueryNameGeneratorInterface;
use ApiPlatform\Core\Metadata\Resource\Factory\ResourceMetadataFactoryInterface;
use Doctrine\Common\Persistence\ManagerRegistry;
use Doctrine\Common\Persistence\ObjectManager;
use Doctrine\ORM\Mapping\ClassMetadataInfo;
use Doctrine\ORM\QueryBuilder;
use Psr\Log\LoggerInterface;
use Symfony\Component\HttpFoundation\RequestStack;
use Symfony\Component\Serializer\NameConverter\NameConverterInterface;

class DiscriminatorFilter extends AbstractContextAwareFilter
{
    const FILTER_RESOURCE_TYPE_PROPERTY = 'type';

    /**
     * @var ResourceMetadataFactoryInterface
     */
    private $resourceMetadataFactory;

    public function __construct(
        ManagerRegistry $managerRegistry,
        ResourceMetadataFactoryInterface $resourceMetadataFactory,
        ?RequestStack $requestStack = null,
        LoggerInterface $logger = null,
        array $properties = null,
        NameConverterInterface $nameConverter = null
    )
    {
        parent::__construct($managerRegistry, $requestStack, $logger, $properties, $nameConverter);

        $this->resourceMetadataFactory = $resourceMetadataFactory;
    }

    /**
     * {@inheritdoc}
     */
    public function getDescription(string $resourceClass): array
    {
        return [
            static::FILTER_RESOURCE_TYPE_PROPERTY => [
                'property' => static::FILTER_RESOURCE_TYPE_PROPERTY,
                'type' => 'string',
                'required' => false,
            ],
        ];
    }

    /**
     * {@inheritdoc}
     */
    protected function filterProperty(
        string $property,
        $value,
        QueryBuilder $queryBuilder,
        QueryNameGeneratorInterface $queryNameGenerator,
        string $resourceClass,
        string $operationName = null,
        array $context = []
    )
    {
        if ($property !== static::FILTER_RESOURCE_TYPE_PROPERTY) {
            return;
        }

        /** @var ObjectManager $manager */
        $manager              = $this->managerRegistry->getManagerForClass($resourceClass);
        $classMetadataFactory = $manager->getMetadataFactory();

        /** @var ClassMetadataInfo $classMetadata */
        $classMetadata = $classMetadataFactory->getMetadataFor($resourceClass);
        foreach ($classMetadata->discriminatorMap as $discriminator => $entityClass) {
            $metadata = $this->resourceMetadataFactory->create($entityClass);
            if (strtolower($metadata->getShortName()) === $value) {
                $alias = $queryBuilder->getAllAliases()[0];
                $queryBuilder->andWhere($alias . ' INSTANCE OF :instance');
                $queryBuilder->setParameter('instance', $discriminator);

                return;
            }
        }

        $queryBuilder->where('1=0');
    }
}

@khaledBou
Copy link

I tried this class, i created a service and i call the service in my entity :

/**
 * A Contact
 *
 * @ApiResource(attributes={
 *     "normalization_context"={"groups"={"contact:read"}},
 *     "denormalization_context"={"groups"={"contact:write"}},
 *     "filters"={"custom.search_filter"} // call the Discriminator filter
 * })
 *

It works for me, i can filter with the DiscriminatorColumn name

Thanks

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment