PHPackages                             isma/api-filters-bundle - PHPackages - PHPackages  [Skip to content](#main-content)[PHPackages](/)[Directory](/)[Categories](/categories)[Trending](/trending)[Leaderboard](/leaderboard)[Changelog](/changelog)[Analyze](/analyze)[Collections](/collections)[Log in](/login)[Sign up](/register)

1. [Directory](/)
2. /
3. [API Development](/categories/api)
4. /
5. isma/api-filters-bundle

ActiveSymfony-bundle[API Development](/categories/api)

isma/api-filters-bundle
=======================

Symfony bundle for API filtering

v1.3.0(2mo ago)126MITPHPPHP &gt;=8.3CI passing

Since Mar 3Pushed 2mo ago1 watchersCompare

[ Source](https://github.com/yagami271/api-filters-bundle)[ Packagist](https://packagist.org/packages/isma/api-filters-bundle)[ RSS](/packages/isma-api-filters-bundle/feed)WikiDiscussions main Synced 1mo ago

READMEChangelog (7)Dependencies (27)Versions (20)Used By (0)

🎯 API Filters Bundle
====================

[](#-api-filters-bundle)

[![CI](https://github.com/yagami271/api-filters-bundle/actions/workflows/ci.yaml/badge.svg)](https://github.com/yagami271/api-filters-bundle/actions/workflows/ci.yaml)[![PHP](https://camo.githubusercontent.com/7abd1166ad32b2aae0a61e90afe2c3f849830e0f9f57d2c3940bfd838f4c046d/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f5048502d254532253839254135382e332d3838393242463f6c6f676f3d706870266c6f676f436f6c6f723d7768697465)](https://www.php.net/)[![Symfony](https://camo.githubusercontent.com/7fba86b0389df5e5f7fe07c476b11896e13860d6c96d55accc927b7da3fae0f6/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f53796d666f6e792d362e34253230253743253230372e34253230253743253230382e302d3030303030303f6c6f676f3d73796d666f6e79266c6f676f436f6c6f723d7768697465)](https://symfony.com/)[![License](https://camo.githubusercontent.com/b078b672949ab89b298228f00ee47e018ec82c7d71f1dca6fec71b9788605992/68747470733a2f2f696d672e736869656c64732e696f2f6769746875622f6c6963656e73652f796167616d693237312f6170692d66696c746572732d62756e646c65)](LICENSE)[![PHPStan](https://camo.githubusercontent.com/14995ff65edea59395c224e37e4fc66f91c1e601c1a58311e3c6f38c4fe37feb/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f5048505374616e2d6c6576656c2532306d61782d627269676874677265656e)](https://phpstan.org/)[![Code Style](https://camo.githubusercontent.com/9049bd4c028ff87e14f7baeeb0030e7521e3f063277bb920b9df88dbc725ef8a/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f636f64652532307374796c652d25343053796d666f6e792d626c61636b)](https://cs.symfony.com/)

A Symfony bundle that resolves API query filters from HTTP requests and applies them to query builders using a strategy pattern. Declare allowed filters with PHP attributes on your controller actions, and the bundle handles parsing, validation, and query building automatically.

📋 Requirements
--------------

[](#-requirements)

- PHP &gt;= 8.3
- Symfony 6.4 / 7.4 / 8.0+
- Doctrine ORM &gt;= 3.4 *(required for the built-in ORM filter strategies)*
- Doctrine DBAL &gt;= 4.0 *(required for the built-in DBAL filter strategies)*
- PDO *(required for the built-in Pure SQL filter strategies — no Doctrine dependency)*

📦 Installation
--------------

[](#-installation)

```
composer require isma/api-filters-bundle
```

If you're using Symfony Flex, the bundle is automatically registered. Otherwise, add it to `config/bundles.php`:

```
return [
    // ...
    Isma\ApiFiltersBundle\ApiFiltersBundle::class => ['all' => true],
];
```

🚀 Quick start
-------------

[](#-quick-start)

### 1. Add `#[ApiFilter]` attributes to your controller

[](#1-add-apifilter-attributes-to-your-controller)

```
use Isma\ApiFiltersBundle\Attribute\ApiFilter;
use Isma\ApiFiltersBundle\ValueObject\Filters;
use Isma\ApiFiltersBundle\ValueObject\FilterType;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\Routing\Attribute\Route;

final class UserController
{
    #[Route('/api/users', methods: ['GET'])]
    #[ApiFilter(name: 'firstname', allowedTypes: [FilterType::Eq->value, FilterType::Like->value])]
    #[ApiFilter(name: 'lastname')]
    #[ApiFilter(name: 'status', enumClass: UserStatus::class)]
    public function list(Filters $filters): JsonResponse
    {
        // $filters is automatically resolved from the query string
    }
}
```

### 2. Send a request with filters

[](#2-send-a-request-with-filters)

```
GET /api/users?filters[firstname][eq]=John
GET /api/users?filters[firstname][like]=Joh
GET /api/users?filters[firstname][ilike]=joh
GET /api/users?filters[firstname][inotlike]=admin
GET /api/users?filters[firstname][ieq]=john
GET /api/users?filters[firstname][inoteq]=john
GET /api/users?filters[firstname][start_with]=Jo
GET /api/users?filters[email][end_with]=@example.com
GET /api/users?filters[status][eq]=active
GET /api/users?filters[age][gte]=18
GET /api/users?filters[age][lt]=65
GET /api/users?filters[deleted_at][is_null]=true
GET /api/users?filters[firstname][eq]=John&filters[lastname][eq]=Doe
GET /api/users?filters[firstname][order]=asc

```

Filters also support arrays (for `eq`, `neq`, `like`, `ilike`, `inotlike`, `ieq`, `inoteq`, `start_with`, `end_with`):

```
GET /api/users?filters[status][eq][]=active&filters[status][eq][]=inactive

```

### 3. Apply filters to a Doctrine query builder

[](#3-apply-filters-to-a-doctrine-query-builder)

```
use Isma\ApiFiltersBundle\Filter\FilterApplierInterface;

final class UserRepository
{
    public function __construct(
        private FilterApplierInterface $filterApplier,
    ) {
    }

    public function findByFilters(Filters $filters): array
    {
        $qb = $this->createQueryBuilder('u');

        $this->filterApplier->apply($qb, $filters, [
            'firstname' => 'u.firstname',
            'lastname'  => 'u.lastname',
            'status'    => 'u.status',
        ]);

        return $qb->getQuery()->getResult();
    }
}
```

⚙️ The `#[ApiFilter]` attribute
-------------------------------

[](#️-the-apifilter-attribute)

ParameterTypeDefaultDescription`name``string`*(required)*The filter field name used in the query string`allowedTypes``string[]``[]` (all types)Restrict which filter types can be used. Empty = all types allowed`enumClass``class-string|null``null`Validate and cast the filter value against a backed enum### Restricting filter types

[](#restricting-filter-types)

```
// Only allow exact match on the "role" field
#[ApiFilter(name: 'role', allowedTypes: [FilterType::Eq->value])]
```

If a client sends a disallowed type, a `400 Bad Request` is returned.

### Enum validation

[](#enum-validation)

```
#[ApiFilter(name: 'status', enumClass: UserStatus::class)]
```

The value is validated against the enum cases. Invalid values return a `400 Bad Request`.

### 4. Apply filters using DBAL (without ORM)

[](#4-apply-filters-using-dbal-without-orm)

If you don't use Doctrine ORM, you can use the DBAL filter applier with a raw `Doctrine\DBAL\Query\QueryBuilder`:

```
use Isma\ApiFiltersBundle\Filter\DBAL\DbalFilterApplierInterface;

final class UserRepository
{
    public function __construct(
        private DbalFilterApplierInterface $filterApplier,
        private Connection $connection,
    ) {
    }

    public function findByFilters(Filters $filters): array
    {
        $qb = $this->connection->createQueryBuilder()
            ->select('t.*')
            ->from('users', 't');

        $this->filterApplier->apply($qb, $filters, [
            'firstname' => 't.firstname',
            'lastname'  => 't.lastname',
            'status'    => 't.status',
        ]);

        return $qb->executeQuery()->fetchAllAssociative();
    }
}
```

### 5. Apply filters using Pure SQL (PDO — no Doctrine required)

[](#5-apply-filters-using-pure-sql-pdo--no-doctrine-required)

If your project doesn't use Doctrine at all, you can use the Pure SQL filter applier with a raw PDO connection:

```
use Isma\ApiFiltersBundle\Filter\SQL\SqlFilterApplierInterface;
use Isma\ApiFiltersBundle\Filter\SQL\SqlQueryContext;

final class UserRepository
{
    public function __construct(
        private SqlFilterApplierInterface $filterApplier,
        private \PDO $pdo,
    ) {
    }

    public function findByFilters(Filters $filters): array
    {
        $context = new SqlQueryContext();

        $this->filterApplier->apply($context, $filters, [
            'firstname' => 'firstname',
            'lastname'  => 'lastname',
            'status'    => 'status',
        ]);

        $sql = 'SELECT * FROM users';
        if ($where = $context->getWhereClause()) {
            $sql .= ' WHERE ' . $where;
        }
        if ($orderBy = $context->getOrderByClause()) {
            $sql .= ' ORDER BY ' . $orderBy;
        }

        $stmt = $this->pdo->prepare($sql);
        foreach ($context->getParameters() as $name => $value) {
            $stmt->bindValue(':' . $name, $value);
        }
        $stmt->execute();

        return $stmt->fetchAll(\PDO::FETCH_ASSOC);
    }
}
```

🔎 Built-in filter types
-----------------------

[](#-built-in-filter-types)

The bundle ships with 15 filter strategies for ORM, DBAL, and Pure SQL:

TypeQuery stringScalar DQLArray DQL`eq``filters[field][eq]=value``field = :param``field IN (:param)``neq``filters[field][neq]=value``field != :param``field NOT IN (:param)``like``filters[field][like]=value``field LIKE '%val%'`OR of `LIKE` clauses`ilike``filters[field][ilike]=value``LOWER(field) LIKE LOWER('%val%')`OR of `LIKE` clauses`inotlike``filters[field][inotlike]=value``LOWER(field) NOT LIKE LOWER('%val%')`AND of `NOT LIKE` clauses`ieq``filters[field][ieq]=value``LOWER(field) = :param``LOWER(field) IN (:param)``inoteq``filters[field][inoteq]=value``LOWER(field) != :param``LOWER(field) NOT IN (:param)``start_with``filters[field][start_with]=value``field LIKE 'val%'`OR of `LIKE` clauses`end_with``filters[field][end_with]=value``field LIKE '%val'`OR of `LIKE` clauses`gt``filters[field][gt]=value``field > :param`❌ throws exception`gte``filters[field][gte]=value``field >= :param`❌ throws exception`lt``filters[field][lt]=value``field < :param`❌ throws exception`lte``filters[field][lte]=value``field andWhere(\sprintf('%s BETWEEN :%s_min AND :%s_max', $column, $parameterName, $parameterName))
            ->setParameter($parameterName.'_min', $value[0])
            ->setParameter($parameterName.'_max', $value[1]);
    }
}
```

The strategy is automatically tagged with `isma_api_filters.strategy` and collected by `OrmFilterApplier`. No manual service registration needed.

### Creating a custom DBAL filter strategy

[](#creating-a-custom-dbal-filter-strategy)

For DBAL, implement `DbalFilterStrategyInterface`:

```
use Doctrine\DBAL\Query\QueryBuilder;
use Isma\ApiFiltersBundle\Filter\DBAL\DbalFilterStrategyInterface;

final class BetweenFilterStrategy implements DbalFilterStrategyInterface
{
    public function getType(): string
    {
        return 'between';
    }

    public function apply(QueryBuilder $queryBuilder, string $column, mixed $value, string $parameterName): void
    {
        // Expects value as [min, max]
        $queryBuilder->andWhere(\sprintf('%s BETWEEN :%s_min AND :%s_max', $column, $parameterName, $parameterName))
            ->setParameter($parameterName.'_min', $value[0])
            ->setParameter($parameterName.'_max', $value[1]);
    }
}
```

The strategy is automatically tagged with `isma_api_filters.dbal_strategy` and collected by `DbalFilterApplier`.

### Creating a custom Pure SQL filter strategy

[](#creating-a-custom-pure-sql-filter-strategy)

For pure SQL (PDO), implement `SqlFilterStrategyInterface`:

```
use Isma\ApiFiltersBundle\Filter\SQL\SqlFilterStrategyInterface;
use Isma\ApiFiltersBundle\Filter\SQL\SqlQueryContext;

final class BetweenFilterStrategy implements SqlFilterStrategyInterface
{
    public function getType(): string
    {
        return 'between';
    }

    public function apply(SqlQueryContext $context, string $column, mixed $value, string $parameterName): void
    {
        // Expects value as [min, max]
        $context->andWhere(\sprintf('%s BETWEEN :%s_min AND :%s_max', $column, $parameterName, $parameterName))
            ->setParameter($parameterName.'_min', $value[0])
            ->setParameter($parameterName.'_max', $value[1]);
    }
}
```

The strategy is automatically tagged with `isma_api_filters.sql_strategy` and collected by `SqlFilterApplier`.

> **Naming convention:** To avoid any collision with current or future built-in filter types, prefix your custom filter names with `x-` (e.g. `x-between`, `x-fulltext`). This ensures your custom strategies will never conflict with a type added to the bundle in a later version.

You can then use it immediately:

```
GET /api/users?filters[age][between][]=18&filters[age][between][]=65

```

⚠️ Error handling
-----------------

[](#️-error-handling)

ScenarioExceptionHTTP statusUnknown filter field`InvalidFilterException`400Disallowed filter type`InvalidFilterException`400Invalid enum value`InvalidFilterException`400Malformed filter format`InvalidFilterException`400Missing field mapping in `apply()``\InvalidArgumentException`500Duplicate strategy for same type`DuplicateFilterStrategyException`Container build error🗺️ Roadmap
----------

[](#️-roadmap)

- **More filter types** — Expand the built-in strategy catalog (e.g. `not_between`, `in_range`, `is_empty`, full-text search, etc.).

📄 License
---------

[](#-license)

MIT - see [LICENSE](LICENSE) for details.

###  Health Score

44

—

FairBetter than 92% of packages

Maintenance88

Actively maintained with recent releases

Popularity12

Limited adoption so far

Community7

Small or concentrated contributor base

Maturity58

Maturing project, gaining track record

 Bus Factor1

Top contributor holds 100% of commits — single point of failure

How is this calculated?**Maintenance (25%)** — Last commit recency, latest release date, and issue-to-star ratio. Uses a 2-year decay window.

**Popularity (30%)** — Total and monthly downloads, GitHub stars, and forks. Logarithmic scaling prevents top-heavy scores.

**Community (15%)** — Contributors, dependents, forks, watchers, and maintainers. Measures real ecosystem engagement.

**Maturity (30%)** — Project age, version count, PHP version support, and release stability.

###  Release Activity

Cadence

Every ~2 days

Total

7

Last Release

60d ago

Major Versions

v0.1.0 → v1.0.02026-03-04

### Community

Maintainers

![](https://www.gravatar.com/avatar/2b36f166ae795f66ac2b9a9a7b0debdfb1d84582505b3688468b8ce19c584782?d=identicon)[yagami271](/maintainers/yagami271)

---

Top Contributors

[![yagami271](https://avatars.githubusercontent.com/u/15124496?v=4)](https://github.com/yagami271 "yagami271 (12 commits)")

---

Tags

symfonysymfony-bundleapisymfonybundlequeryfilter

###  Code Quality

TestsPHPUnit

Static AnalysisPHPStan

Code StylePHP CS Fixer

Type Coverage Yes

### Embed Badge

![Health badge](/badges/isma-api-filters-bundle/health.svg)

```
[![Health](https://phpackages.com/badges/isma-api-filters-bundle/health.svg)](https://phpackages.com/packages/isma-api-filters-bundle)
```

###  Alternatives

[sylius/sylius

E-Commerce platform for PHP, based on Symfony framework.

8.4k5.6M651](/packages/sylius-sylius)[sulu/sulu

Core framework that implements the functionality of the Sulu content management system

1.3k1.3M152](/packages/sulu-sulu)[stfalcon-studio/api-bundle

Base classes and helper services to build API application via Symfony.

1032.1k](/packages/stfalcon-studio-api-bundle)

PHPackages © 2026

[Directory](/)[Categories](/categories)[Trending](/trending)[Changelog](/changelog)[Analyze](/analyze)
