PHPackages                             alex-patterson-webdev/doctrine-query-filter - 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. [Database &amp; ORM](/categories/database)
4. /
5. alex-patterson-webdev/doctrine-query-filter

ActiveLibrary[Database &amp; ORM](/categories/database)

alex-patterson-webdev/doctrine-query-filter
===========================================

Query filtering components for Doctrine ORM

0.9.0(2y ago)71.2k11MITPHPPHP &gt;=8.2

Since Feb 27Pushed 2y ago1 watchersCompare

[ Source](https://github.com/alex-patterson-webdev/doctrine-query-filter)[ Packagist](https://packagist.org/packages/alex-patterson-webdev/doctrine-query-filter)[ RSS](/packages/alex-patterson-webdev-doctrine-query-filter/feed)WikiDiscussions master Synced today

READMEChangelog (9)Dependencies (9)Versions (10)Used By (1)

[![build](https://github.com/alex-patterson-webdev/doctrine-query-filter/actions/workflows/workflow.yml/badge.svg)](https://github.com/alex-patterson-webdev/doctrine-query-filter/actions/workflows/workflow.yml/badge.svg)[![codecov](https://camo.githubusercontent.com/16a614d9706bb676bb5372495a313e1694671a09a01b0efe545a79c9ac19180c/68747470733a2f2f636f6465636f762e696f2f67682f616c65782d706174746572736f6e2d7765626465762f646f637472696e652d71756572792d66696c7465722f6272616e63682f6d61737465722f67726170682f62616467652e737667)](https://codecov.io/gh/alex-patterson-webdev/doctrine-query-filter)[![Scrutinizer Code Quality](https://camo.githubusercontent.com/13ee3a1ed8127cd3cbe341c70c17bc5cce4b7ddcf2e38ae6318c54502b9974bc/68747470733a2f2f7363727574696e697a65722d63692e636f6d2f672f616c65782d706174746572736f6e2d7765626465762f646f637472696e652d71756572792d66696c7465722f6261646765732f7175616c6974792d73636f72652e706e673f623d6d6173746572)](https://scrutinizer-ci.com/g/alex-patterson-webdev/doctrine-query-filter/?branch=master)

Doctrine Query Filter
=====================

[](#doctrine-query-filter)

A package providing query filtering components for Doctrine ORM. By modeling query filter criteria as reusable objects, it offers a consistent and extendable way of constructing complex DQL statements.

The project has been inspired by the [Laminas Doctrine QueryBuilder](https://github.com/laminas-api-tools/api-tools-doctrine-querybuilder); providing similar functionality without the Laminas Framework dependency.

Installation
------------

[](#installation)

Installation via [composer](https://getcomposer.org).

```
require alex-patterson-webdev/doctrine-query-filter ^0.9

```

Usage
-----

[](#usage)

Using the `QueryFilterManager` we can create DQL strings from an `array` format. For example, consider the following DQL string.

```
SELECT c FROM Customer c WHERE c.forename = 'Fred' AND (c.age BETWEEN 18 AND 30)

```

We can represent this DQL query using a collection of *filters*, known as our query *criteria*

```
$criteria = [
    'filters' => [
        [
            'name' => 'eq',
            'field' => 'forename',
            'value' => 'Fred',
        ],
        [
            'name' => 'between',
            'field' => 'age',
            'from' => 18,
            'to' => 30
        ],
    ],
];

```

By passing this `$criteria` to our `QueryFilterManager` we can generate (and execute) the query in the following way.

```
// Get our Doctrine query builder instance
$queryBuilder = $entityManager->getRepository('Customer')->createQueryBuilder('c');

// Create a new QueryFilterManager (and supply it with a desired FilterFactory instance)
$queryFilterManager = new QueryFilterManager(new FilterFactory());

// Apply the filters to the $queryBuilder
$queryBuilder = $queryFilterManager->filter($queryBuilder, 'Customer', $criteria);

// SELECT c FROM Customer c WHERE c.forename = 'Fred' AND (c.age BETWEEN 18 AND 30)
echo $queryBuilder->getDQL();

// Fetch the results
$results = $queryBuilder->getQuery()->execute();

```

### Combining filters with an OR condition

[](#combining-filters-with-an-or-condition)

When defining more than one filter, conditions will be explicitly "AND" together using the `and` composite query filter. To instead create an "OR" condition, we must define a `or` filter and provide it with the required `conditions` array.

```
// SELECT c FROM Customer c WHERE c.enabled = :enabled AND (c.username = :username1 OR c.username = :username2)
$criteria = [
    'filters' => [
        [
            'name' => 'eq',
            'field' => 'enabled',
            'value' => true,
        ],
        [
            'name' => 'or',
            'conditions' => [
                [
                    'name' => 'eq',
                    'field' => 'username',
                    'value' => 'Fred',
                ],
                [
                    'name' => 'eq',
                    'field' => 'username',
                    'value' => 'bob',
                ],
            ]
        ],
    ],
];

```

### Nesting Filters

[](#nesting-filters)

You can also nest a combination of the `and` and `or`, the generated DQL will include the correct grouping.

```
// WHERE x.surname = 'Smith' OR (x.age > 18 AND x.gender = 'Male')
$criteria = [
    'filters' => [
        [
            'name' => 'or',
            'conditions' => [
                [
                    'name' => 'eq',
                    'field' => 'surname',
                    'value' => 'Smith',
                ],
                [
                    'name' => 'and',
                    'conditions' => [
                        [
                            'name' => 'gt',
                            'field' => 'age',
                            'value' => 18,
                        ],
                        [
                            'name' => 'eq'
                            'field' => 'gender',
                            'value' => 'Male',
                        ],
                    ]
                ],
            ]
        ]
    ],
];

```

Custom Filters
--------------

[](#custom-filters)

The above examples demonstrate the use of the built-in filters. However, these are very verbose and can be difficult to manage. The true power of the `QueryFilterManager` is the ability to create and use custom filters; by extending the `AbstractFilter` class. Custom filters are self-contained and reusable across multiple queries. This allows for a more modular and maintainable approach to build complex queries.

The below example demonstrates how we could utilise the provided filters to create our own `CustomerSearch` filter that accepts optional `$criteria` parameters.

```
use Arp\DoctrineQueryFilter\Filter\AbstractFilter;
use Arp\DoctrineQueryFilter\Filter\Exception\FilterException;
use Arp\DoctrineQueryFilter\Metadata\MetadataInterface;
use Arp\DoctrineQueryFilter\QueryBuilderInterface;

final class CustomerSearch extends AbstractFilter
{
    public function filter(QueryBuilderInterface $queryBuilder, MetadataInterface $metadata, array $criteria): void
    {
        if (empty($criteria['surname'])) {
            throw new FilterException('The surname criteria is required');
        }

        $filters = [
            [
                'name' => 'neq',
                'field' => 'status',
                'value' => 'inactive',
            ],
            [
                'name' => 'begins_with',
                'field' => 'surname',
                'value' => $criteria['surname'],
            ],
        ];

        if (isset($criteria['forename'])) {
            $filters[] = [
                'name' => 'eq',
                'field' => 'forename',
                'value' => $criteria['forename'],
            ];
        }

        if (isset($criteria['age'])) {
            $filters[] = [
                'name' => 'gte',
                'field' => 'age',
                'value' => (int) $criteria['age'],
            ];
        }

        $this->applyFilters($queryBuilder, $metadata, $filters);
    }
}

// We must register the custom filter with the FilterFactory
$filterFactory = new FilterFactory();
$filterFactory->addToClassMap('customer_search', CustomerSearch::class);

$queryFilterManager = new QueryFilterManager($filterFactory);
$criteria = [
    'filters' => [
        [
            'name' => 'customer_search',
            'surname' => 'Smith',
            'age' => 21,
        ],
    ],
];

$queryBuilder = $queryFilterManager->filter($queryBuilder, 'Entity\Customer', $criteria);

// Executes DQL: SELECT c FROM Customer c WHERE c.status != 'inactive' AND c.surname LIKE 'Smith%' AND c.age >= 21
$queryBuilder->getQuery()->execute();

```

Sorting Results
---------------

[](#sorting-results)

In addition to filtering collections, we can also add sorting by using the `sort` criteria key to add Sort Fillers.

```
// SELECT c FROM Customer c WHERE c.id = 123 ORDER BY c.id DESC, c.createdDate ASC
$critiera = [
    'filters' => [
        [
            'name' => 'eq',
            'field' => 'id',
            'value' => 123
        ],
        'sort' => [
            [
                'name' => Field::class,
                'field' => 'id',
                'direction' => OrderByDirection::DESC->value
            ],
            [
                'field' => 'createdDate'
            ],
        ]
    ]
];

```

Each sort filter requires the `field` key, with an optional `direction` of `ASC` or `DESC`. Omitting the `name` key from a sort filter will apply a `Arp\DoctrineQueryFilter\Sort\Field` sort filter by default. In addition, omitting the `direction` will by default make the sort direction `ASC`.

Filter Reference
----------------

[](#filter-reference)

There are many types of query filters already included. The table below defines the filter aliases and their available options.

AliasClass NameDescriptionRequired OptionsOptional OptionseqArp\\DoctrineQueryFilter\\Filter\\IsEqualTest is `field` = `value``field`, `value``alias`, `format`neqArp\\DoctrineQueryFilter\\Filter\\IsNotEqualTest is `field` != `value``field`, `value``alias`, `format`gtArp\\DoctrineQueryFilter\\Filter\\IsGreaterThanTest is `field` &gt; `value``field`, `value``alias`, `format`gteArp\\DoctrineQueryFilter\\Filter\\IsGreaterThanOrEqualTest is `field` &gt;= `value``field`, `value``alias`, `format`ltArp\\DoctrineQueryFilter\\Filter\\IsLessThanTest is `field` &lt; `value``field`, `value``alias`, `format`lteArp\\DoctrineQueryFilter\\Filter\\IsLessThanOrEqualTest is `field` &lt;= `value``field`, `value``alias`, `format`andArp\\DoctrineQueryFilter\\Filter\\AndXJoin two or more expressions using logical AND`conditions`orArp\\DoctrineQueryFilter\\Filter\\OrXJoin two or more expressions using logical OR`conditions`betweenArp\\DoctrineQueryFilter\\Filter\\IsBetweenTest if `field` =&gt; `from` and `field` &lt;= `to``field`, `from`, `to``alias`, `format`member\_ofArp\\DoctrineQueryFilter\\Filter\\IsMemberOfTest if `value` exists within collection `field``field`, `value``alias`, `format`is\_nullArp\\DoctrineQueryFilter\\Filter\\IsNullTest if `field` is NULL`field``alias`, `format`not\_nullArp\\DoctrineQueryFilter\\Filter\\IsNotNullTest if `field` is NOT NULL`field``alias`, `format`likeArp\\DoctrineQueryFilter\\Filter\\IsLikeTest if `field` is LIKE `value``field`, `value``alias`, `format`not\_likeArp\\DoctrineQueryFilter\\Filter\\IsNotLikeCheck if `field` is NOT LIKE `field``field`, `value``alias`, `format`inArp\\DoctrineQueryFilter\\Filter\\IsInCheck if `field` is IN `field``field`, `value``alias`, `format`not\_inArp\\DoctrineQueryFilter\\Filter\\IsNotInCheck if `field` is NOT IN `value``field`, `value``alias`, `format`begins\_withArp\\DoctrineQueryFilter\\Filter\\BeginsWithCheck if `field` beings with `value``field`, `value``alias`, `format`ends\_withArp\\DoctrineQueryFilter\\Filter\\EndsWithCheck if `field` ends with `value``field`, `value``alias`, `format`emptyArp\\DoctrineQueryFilter\\Filter\\IsEmptyCheck if `field` is equal to ('' or NULL)`field`left\_joinArp\\DoctrineQueryFilter\\Filter\\LeftJoinApply left join to `field` with optional conditions`field``alias`, `conditions`, `condition_type`, `index_by`inner\_joinArp\\DoctrineQueryFilter\\Filter\\InnerJoinApply inner join to `field` with optional conditions`field``alias`, `conditions`, `condition_type`, `index_by`FilterFactory
-------------

[](#filterfactory)

If you require greater control on the construction of the query filters, it is possible to provide `QueryFilter`instances directly to the `$criteria['filters']` array instead of using the array format.

```
$queryFilterManager = new QueryFilterManager(new FilterFactory());
$criteria = [
    'filters' => [
        $queryFilterManager->createFilter('eq', ['field' => 'surname', 'value => 'Smith']),
        $queryFilterManager->createFilter('between', ['field' => 'age', 'from => 18, 'to' => 65]),
    ],
],

```

Unit tests
----------

[](#unit-tests)

Unit tests can be executed using PHPUnit from the application root directory.

```
php vendor/bin/phpunit

```

###  Health Score

32

—

LowBetter than 69% of packages

Maintenance20

Infrequent updates — may be unmaintained

Popularity25

Limited adoption so far

Community10

Small or concentrated contributor base

Maturity61

Established project with proven stability

 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 ~134 days

Recently: every ~177 days

Total

9

Last Release

874d ago

PHP version history (3 changes)0.1.0PHP &gt;=7.4 || &gt;=8.0

0.8.0PHP &gt;=8.1

0.9.0PHP &gt;=8.2

### Community

Maintainers

![](https://avatars.githubusercontent.com/u/1563851?v=4)[Alex Patterson](/maintainers/alex-patterson-webdev)[@alex-patterson-webdev](https://github.com/alex-patterson-webdev)

---

Top Contributors

[![alex-patterson-webdev](https://avatars.githubusercontent.com/u/1563851?v=4)](https://github.com/alex-patterson-webdev "alex-patterson-webdev (131 commits)")

###  Code Quality

TestsPHPUnit

Static AnalysisPHPStan

Code StylePHP CS Fixer

Type Coverage Yes

### Embed Badge

![Health badge](/badges/alex-patterson-webdev-doctrine-query-filter/health.svg)

```
[![Health](https://phpackages.com/badges/alex-patterson-webdev-doctrine-query-filter/health.svg)](https://phpackages.com/packages/alex-patterson-webdev-doctrine-query-filter)
```

###  Alternatives

[easycorp/easyadmin-bundle

Admin generator for Symfony applications

4.3k17.9M387](/packages/easycorp-easyadmin-bundle)[rcsofttech/audit-trail-bundle

Enterprise-grade, high-performance Symfony audit trail bundle. Automatically track Doctrine entity changes with split-phase architecture, multiple transports (HTTP, Queue, Doctrine), and sensitive data masking.

1189.8k](/packages/rcsofttech-audit-trail-bundle)[kimai/kimai

Kimai - Time Tracking

4.8k9.0k1](/packages/kimai-kimai)[2lenet/crudit-bundle

The easy like Crud'it Bundle.

1616.4k14](/packages/2lenet-crudit-bundle)

PHPackages © 2026

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