sidus / doctrine-filter-bundle
Doctrine ORM provider for sidus/filter-bundle: criteria compiled to deterministic DQL
Package info
github.com/VincentChalnot/SidusDoctrineFilterBundle
Type:symfony-bundle
pkg:composer/sidus/doctrine-filter-bundle
Requires
- php: >=8.4
- doctrine/dbal: ^4.0
- doctrine/doctrine-bundle: ^3.0
- doctrine/orm: ^3.3
- doctrine/persistence: ^3.3 || ^4.0
- sidus/filter-bundle: ^7.0
- symfony/config: ^8.1
- symfony/dependency-injection: ^8.1
- symfony/http-kernel: ^8.1
- symfony/options-resolver: ^8.1
Requires (Dev)
- martin-georgiev/postgresql-for-doctrine: ^4.9
- phpunit/phpunit: ^12.0
- symfony/console: ^8.1
- symfony/doctrine-bridge: ^8.1
- symfony/filesystem: ^8.1
- symfony/form: ^8.1
- symfony/framework-bundle: ^8.1
- symfony/yaml: ^8.1
Suggests
- martin-georgiev/postgresql-for-doctrine: JSON path filtering and sorting on PostgreSQL (json/jsonb columns)
- symfony/console: Enables the lint:sidus-filter command
- symfony/doctrine-bridge: EntityType, for the form mapper of the relation filter
- symfony/form: Form adapter of sidus/filter-bundle (relation filter mapper)
Provides
None
Conflicts
None
Replaces
None
README
The Doctrine ORM provider (provider: doctrine) of sidus/filter-bundle
7.0: every criterion of a list is compiled to deterministic DQL, with the same meaning as on the other providers.
PHP 8.4+, Symfony 8.1+, Doctrine ORM 3.3+ / DBAL 4, DoctrineBundle 3. PostgreSQL, SQLite and MariaDB / MySQL.
- No row multiplication. To-one paths share one
LEFT JOINper prefix with deterministic aliases (author_,author_company_); every to-many criterion is its own correlatedEXISTS, so two criteria on a collection are independent ("some tag isphpand some tag issymfony"), pages are a plainLIMIT/OFFSETand the total is a plainCOUNT. - Deterministic queries. Aliases come from the paths, parameters are
p1..pnin definition order, nouniqid(): the same input shape is the same DQL, so Doctrine's query cache hits. - Strict. What the platform or the field's type cannot do with the reference meaning is refused, with a reason, at
cache:warmupand inlint:sidus-filter: never approximated, never an exception on a visitor's request. - Worker-mode safe. The container holds only immutable objects (the factory, the compilers, memoised field metadata); the query, its joins and its parameters live in a per-request context.
- Platform-exact. String equality,
LIKE, sorting and regexps behave the same on PostgreSQL, SQLite and MariaDB (whose default collations are case- and accent-insensitive), proved by the core's shared contract suite.
Installation
composer require sidus/doctrine-filter-bundle
Register the bundles (after DoctrineBundle and SidusFilterBundle):
// config/bundles.php Sidus\FilterBundle\SidusFilterBundle::class => ['all' => true], Sidus\DoctrineFilterBundle\SidusDoctrineFilterBundle::class => ['all' => true],
Optional: martin-georgiev/postgresql-for-doctrine (JSON paths on PostgreSQL), symfony/form +
symfony/doctrine-bridge (form field of the relation filter), symfony/console (lint:sidus-filter).
Quick start
# config/packages/sidus_filter.yaml sidus_filter: lists: posts: provider: doctrine options: { entity: App\Entity\Post } pagination: { per_page: 15, max_per_page: 100 } filters: title: { type: text, options: { operators: [contains, starts_with, equals] } } author: { type: text, attributes: [author.name, author.company.name] } tag: { type: exact, attributes: [tags.label] } forum: { type: relation, attributes: [topic.forum], options: { class: App\Entity\Forum } } status: { type: choice, options: { choices: [draft, published] }, default: published, locked: true } sort: type: sort options: columns: { title: title, author: author.name, comments: { field: comments, provider_options: { doctrine: { aggregate: count } } } } default: [{ column: title, direction: asc }]
$result = $registry->createQueryHandler('posts') // a new, single-use handler ->withCustomizer(static function (DoctrineQueryContext $q): void { $author = $q->joins()->aliasFor('author'); // the shared LEFT JOIN $q->queryBuilder()->addSelect($author.'.name AS HIDDEN authorName'); }) ->handle(ListInput::fromArray($request->query->all()));
bin/console lint:sidus-filter # every attribute path, criterion and sort column against the metadata
Documentation
- docs/provider.md: options, field resolution, join strategy, every criterion's DQL, sorting, count,
customizers, named joins, JSON paths, the
relationfilter, computed sorts, platforms and their differences. - docs/testing.md: the test suites, running them on PostgreSQL and MariaDB.
- CHANGELOG.md
Tests
docker run --rm -u $(id -u):$(id -g) -e HOME=/tmp -v $PWD/..:/bundles -w /bundles/SidusDoctrineFilterBundle \ --entrypoint php <image with pdo_sqlite> vendor/bin/phpunit
Until sidus/filter-bundle 7.0 is tagged, composer.json consumes it from the sibling directory
../SidusFilterBundle through a Composer path repository: drop the repositories entry once it is.