Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Form Filter Bundle Laravel Package

spiriitlabs/form-filter-bundle

SpiriitLabs FormFilterBundle is a Symfony bundle that turns standard Symfony forms into reusable data filters. Map form fields to Doctrine query conditions, keep controllers clean, and quickly build search, listing, and admin pages with configurable filtering.

View on GitHub
Deep Wiki
Context7

Getting Started

This release (v12.3.0) is a documentation-and-tooling release — no API changes, no deprecations. The two changes that matter day-to-day are a new VitePress documentation site and a new profiler panel that traces filter state persistence.

Before you write code, calibrate your expectations: spiriitlabs/form-filter-bundle is a Symfony bundle (the maintained continuation of the Lexik form-filter lineage). It builds Doctrine queries from Symfony Form data. In a pure Laravel app you cannot "install and go" — there is no service provider, no config/form-filter.php, no Blade/Eloquent integration. You leverage it in three realistic situations:

  1. A Symfony app (or Symfony + Laravel side-by-side where Symfony owns the admin/filtering surface).
  2. A Laravel app that already pulls in symfony/form for standalone form handling and wants declarative, reusable filter forms.
  3. As a reference implementation for how to structure declarative filters — worth reading before you hand-roll filters with spatie/laravel-query-builder.

First steps for a Symfony codebase:

composer require spiriitlabs/form-filter-bundle

Then register the bundle in config/bundles.php (Symfony Flex handles this), confirm the bundle's YAML block exists under config/packages/, and open the new VitePress docs first — the docs rewrite in PR #54 is the canonical starting point and is now more reliable than blog posts referencing the old Lexik namespace.

Your first use case should be the smallest one: a single TextFilterType field bound to a form, applied to a QueryBuilder in a controller. Get one filter working end-to-end, then add operators, choices, and ranges. Do not start with a 12-field filter form — when something is silently ignored you will have no idea which field caused it.

Implementation Patterns

Filter form + QueryBuilder updater. The core loop is: build a form out of filter types → take the submitted/valid data → hand the form and a QueryBuilder to the updater, which walks the form children and calls andWhere/orWhere for each populated filter. Keep this in a dedicated repository method or a small query-filter service rather than inline in the controller.

One filter type per field, not per column type. Reach for the specific sub-types (TextFilterType, ChoiceFilterType, NumberFilterType, DateFilterType, BooleanFilterType, SearchFilterType) instead of a generic type — they carry the right operators and the right "is empty" semantics.

Compose with Collection/range for multi-value filters. Range and collection filters are where this bundle earns its keep over manual when() chains: one form field maps to a BETWEEN or IN clause with correct parameter binding.

Persist filter state in the session/query string, then let the new profiler panel verify it. PR #53 ("trace filter state persistence") adds a data-collector panel showing how filter state was carried across requests. If you implement "remember my filters" behavior (session key or a serialized query string in the URL), open the profiler after a submit and confirm the restored state matches what you expect before debugging why the query looks wrong.

Extend by subclassing filter types. When you need a custom operator set or a bespoke apply_filter behavior, subclass the closest built-in filter type and override its options rather than branching inside a controller. This keeps the profiler output and the QueryBuilder updater on the happy path.

Integration tip for Laravel-only teams: if you adopt the standalone symfony/form route, wrap the bundle's filter form in a small adapter that converts your Laravel request into a Symfony Request and back. Isolate that shim in one class; everything else (the form definitions, the filter types) is plain PHP and testable in isolation.

Gotchas and Tips

  • Version numbers lie about ecosystem. v12.3.0 looks like a Laravel-major-version cadence but this is a Symfony bundle. Check composer.json for symfony/form and doctrine/orm constraints before assuming it drops into your stack.
  • Namespace migrations bite. Older tutorials use the Lexik\Bundle\FormFilterBundle\... namespace. Copy-pasted snippets will fatal on autoload. Use the SpiriitLabs\FormFilterBundle namespaces shown in the new VitePress docs.
  • Empty filters are the classic bug. A filter with an empty value should be skipped, not produce WHERE field IS NULL or = ''. If a filter appears to "do nothing," it is usually the empty-value guard, not the operator. Verify with the Symfony profiler's Doctrine panel by comparing the generated DQL before/after.
  • Profiler panel is dev-only. The new tracing collector (PR #53) adds request-time overhead and state inspection that has no business in production. Confirm it is absent/disabled under prod and that your dev cache is cleared after upgrading — data collectors frequently fail to appear until bin/console cache:clear.
  • Ordering and andWhere accumulation. Filter conditions are appended, not replaced. Combining filters with hand-written DQL that also calls andWhere makes SQL ordering unpredictable; keep manual clauses out of the same QueryBuilder until the filters are applied.
  • Joins are the sharp edge. Filters on associations may need explicit join configuration on the filter type; without it you get "unrecognized field" DQL errors that look like Doctrine problems but are filter-metadata problems.
  • Debugging loop that works: dump the submitted filter data, then dump the QueryBuilder's DQL and parameters immediately after applying, then cross-reference with the profiler panel. In that order — the panel tells you what state was persisted, not what your form produced.
  • Docs are now versioned. With VitePress in place, bookmark the specific version path rather than the repo README; the README lags behind for options and operator tables.
  • No upgrade action required for v12.3.0. Patch-level, additive only. Safe to bump in CI; just re-run your filter feature tests and confirm the profiler panel renders in dev to validate the toolchain.
Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
diephp/sequences
users-au/socialite-provider
twidpay/twid-logger
mrnewport/laravel-plaid
mrnewport/laravel-repo
mrnewport/laravel-docsign
mrnewport/laravel-stow
mrnewport/laravel-flow
mrnewport/laravel-priceable
mrnewport/laravel-nestedterms
fredmeng/laravel-apidoc-generator
itstructure/laravel-multilingual-tools
pdustdar/elasticsearch
markupai/php-sdk
rlacerda83/lumem-microservice-shipment
uni-method/symfony-lib
tacticmedia/scim-server-bundle
neika-ai/symfony-bundle
mattleads/agent-publishing-bundle
glavweb/framework-bundle