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.0k11MITPHPPHP &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 1mo ago

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 72% 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

827d 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

[scienta/doctrine-json-functions

A set of extensions to Doctrine that add support for json query functions.

58723.9M36](/packages/scienta-doctrine-json-functions)[damienharper/auditor-bundle

Integrate auditor library in your Symfony projects.

4542.8M](/packages/damienharper-auditor-bundle)[sonata-project/entity-audit-bundle

Audit for Doctrine Entities

644989.8k1](/packages/sonata-project-entity-audit-bundle)[pixelfederation/doctrine-resettable-em-bundle

Symfony bundle for decorating default entity managers using a resettable decorator.

20113.5k](/packages/pixelfederation-doctrine-resettable-em-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.

1022.4k](/packages/rcsofttech-audit-trail-bundle)[ahmed-bhs/doctrine-doctor

Runtime analysis tool for Doctrine ORM integrated into Symfony Web Profiler. Unlike static linters, it analyzes actual query execution at runtime to detect performance bottlenecks, security vulnerabilities, and best practice violations during development with real execution context and data.

813.1k](/packages/ahmed-bhs-doctrine-doctor)

PHPackages © 2026

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