Search code examples
filterdtoapi-platform.com

Is there a way to add filters to custom endpoint with DTO?


I have a custom endpoint (which does some custom aggregations), the return of this endpoint is a collection of DTO. I want to add some filters sugestions for the consumers of my api. Is this possible ? How can you do that ?

To sum up :

  • I have a DTO (ApiResource but not linked to doctrine or a database).
  • I have a custom GET endpoint that return a collection of DTO (filtered or not).
  • I want to add filters sugestion to this endpoint.

Should i modify the hydra:search somehow ?

I tried to add ApiFilters (like i do for the entites) on my DTO but ApiFilters are linked to doctrine so it gives me the following error : Call to a member function getClassMetadata() on null on vendor/api-platform/core/src/Bridge/Doctrine/Common/PropertyHelperTrait.php


Solution

  • I encountered the same issue, to solve it I create a custom filter without the $this->isPropertyMapped part.

    I injected the ApiPlatform\Core\Bridge\Doctrine\Orm\Extension\FilterExtension to my collection provider and apply my filters with $this->filterExtension->applyToCollection($qb, $queryNameGenerator, $resourceClass, $operationName, $context); in order to alter the query.

    Then I just need to configure my custom filter in my dto object

    @ApiFilter(SearchFilter::class, properties={"columnName": "exact"})

    <?php
    
    declare(strict_types=1);
    
    namespace App\ThirdParty\ApiPlatform\Filter;
    
    use ApiPlatform\Core\Bridge\Doctrine\Orm\Filter\AbstractContextAwareFilter;
    use ApiPlatform\Core\Bridge\Doctrine\Orm\Util\QueryNameGeneratorInterface;
    use Doctrine\ORM\QueryBuilder;
    
    final class SearchFilter extends AbstractContextAwareFilter
    {
    
        protected function filterProperty(
            string $property,
            $value,
            QueryBuilder $queryBuilder,
            QueryNameGeneratorInterface $queryNameGenerator,
            string $resourceClass,
            string $operationName = null
        ): void {
            if (
                !$this->isPropertyEnabled($property, $resourceClass)
            ) {
                return;
            }
    
            $parameterName = $queryNameGenerator->generateParameterName($property);
    
            $rootAlias = $queryBuilder->getRootAliases()[0];
    
            $queryBuilder
                ->andWhere(sprintf('%s.%s = :%s', $rootAlias, $property, $parameterName))
                ->setParameter($parameterName, $value);
        }
    
        public function getDescription(string $resourceClass): array
        {
            if (!$this->properties) {
                return [];
            }
    
            $description = [];
            foreach ($this->properties as $property => $strategy) {
                $description["regexp_$property"] = [
                    'property' => $property,
                    'type' => 'string',
                    'required' => false,
                    'swagger' => [
                        'description' => 'description',
                        'name' => $property,
                        'type' => 'type',
                    ],
                ];
            }
    
            return $description;
        }
    }