A Custom Filter
A Custom Filter
~15 Min. Lesezeit Zuletzt aktualisiert am August 8, 2026
The built-in filters cover MOST cases – for a full-text search across MULTIPLE fields with ONE single parameter, a CUSTOM filter is needed.
When a custom filter is needed
- ONE parameter should search SEVERAL fields AT ONCE (e.g.
?q=relaunchsearches bothnameANDdescription). - The filtering logic depends on MORE than a simple database comparison (e.g. a similarity search).
- Computed/virtual fields from chapter 25 should ALSO be filterable (the built-in filters work ONLY on real database columns).
Creating the filter class
<?php
declare(strict_types=1);
namespace App\Filter;
use ApiPlatform\Doctrine\Orm\Filter\AbstractFilter;
use ApiPlatform\Metadata\Operation;
use Doctrine\ORM\QueryBuilder;
final class MultiFieldSearchFilter extends AbstractFilter
{
protected function filterProperty(
string $property,
mixed $value,
QueryBuilder $queryBuilder,
\ApiPlatform\Doctrine\Orm\Util\QueryNameGeneratorInterface $queryNameGenerator,
string $resourceClass,
?Operation $operation = null,
array $context = [],
): void {
if ('q' !== $property || !\is_string($value)) {
return;
}
$alias = $queryBuilder->getRootAliases()[0];
$parameter = $queryNameGenerator->generateParameterName('q');
$queryBuilder
->andWhere("{$alias}.name LIKE :{$parameter} OR {$alias}.description LIKE :{$parameter}")
->setParameter($parameter, '%' . $value . '%');
}
public function getDescription(string $resourceClass): array
{
return [
'q' => [
'property' => null,
'type' => 'string',
'required' => false,
'description' => 'Searches name AND description simultaneously.',
],
];
}
}filterProperty() gets called for EVERY query parameter that matches the resource – the check 'q' !== $property ensures the filter reacts ONLY to its OWN q parameter. getDescription() provides the metadata for Swagger UI, EXACTLY as with the built-in filters.
Registering the filter
use App\Filter\MultiFieldSearchFilter;
#[ApiFilter(MultiFieldSearchFilter::class)]
NO properties array needed – the filter defines its OWN parameter name ITSELF via getDescription().
Testing the custom filter
curl -k 'https://localhost/api/projects?q=redesign'Finds projects where "redesign" appears EITHER in the name OR in the description – ONE parameter instead of two separate SearchFilter requests.
Tipp: The AbstractFilter scaffolding is DELIBERATELY built similarly to a QueryBuilder from the Symfony course (chapter 21) – ANYONE who's already written custom repository methods with QueryBuilder there will feel right at home here.