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

Http Query String Filter Bundle Laravel Package

gl3n/http-query-string-filter-bundle

A Symfony bundle for filtering HTTP query string parameters, letting you validate, sanitize, and normalize incoming request data before it reaches your application logic. Keeps controllers clean by centralizing query-string filtering rules.

View on GitHub
Deep Wiki
Context7

Getting Started

This is a small, single-purpose Symfony bundle that turns the messy string soup of $request->query into validated, typed, authorized values. Treat the README and the bundle's own Tests/ directory as the primary documentation — with a package this size, the test cases are usually more accurate than the prose.

Install (Symfony 2.x/3.x era, which is what this targets):

composer require gl3n/http-query-string-filter-bundle

Register the bundle in app/AppKernel.php:

$bundles = [
    // ...
    new Gl3n\HttpQueryStringFilterBundle\HttpQueryStringFilterBundle(),
];

Then confirm the service is wired — look in Resources/config/ for the service definition and run:

php bin/console debug:container | grep -i query

Where to look first, in order:

  1. README.md — the annotated usage example is the whole API surface.
  2. Tests/ — actual signatures, constraint names, and expected casting behavior.
  3. Resources/config/services.xml (or .yml) — the service id you'll inject.
  4. DependencyInjection/Configuration.php — any bundle-level config keys.

First use case — a filtered collection endpoint. The canonical scenario is a paginated list where you accept ?page=2&limit=50&sort=-created_at&active=1 and need to reject bad input, cast page/limit to integers, and refuse sort values the caller isn't authorized to use.

Instead of (int) $request->query->get('page', 1) scattered through the controller, you declare the expected parameters once, attach Symfony Validator constraints, and cast to native types. The bundle gives you back a clean, typed bag you can pass straight into a repository query. That single replace is the entire day-to-day value: one declaration replaces a dozen defensive get() calls.

Implementation Patterns

Declare the filter once, per action

The typical pattern is a declarative spec attached to the controller action (annotation) or built via the injected service. The shape is:

/**
 * @QueryStringFilter({
 *   "page":  {type: "int",    default: 1,   constraints: {"Range": {min: 1}}},
 *   "limit": {type: "int",    default: 25,  constraints: {"Range": {min: 1, max: 100}}},
 *   "sort":  {type: "string", default: "created_at", constraints: {"Choice": {choices: {"created_at", "name", "-created_at"}}}},
 *   "q":     {type: "string", required: false}
 * })
 */
public function listAction(Request $request) { /* ... */ }

(Exact annotation/option names come from the README — the structure above is the mental model: name → type → default → constraints.)

Let validation and casting share one pass

The bundle's real win is that casting happens after validation, so page arrives as int 3, not the string "3", and only if it passed Range. Do not re-validate in the controller — that defeats the point and creates two sources of truth.

Authorization belongs with the filter

For parameters that gate data access (e.g. ?tenant_id=, ?include_deleted=1), attach the authorization check to the parameter itself. This keeps the "can this user even ask for this?" decision next to the "is this well-formed?" decision, and yields the correct HTTP semantics: malformed → 400, forbidden → 403. Push it into a security expression where possible (is_granted('ROLE_ADMIN') style) so the rule lives in security config, not controller code.

Compose with a repository/query-builder

$criteria = $this->queryFilter->filter($request);   // typed, validated array
$qb = $this->repo->createQueryBuilder('e')
    ->setFirstResult(($criteria['page'] - 1) * $criteria['limit'])
    ->setMaxResults($criteria['limit']);

Because the output is a plain array of native types, it slots into a Criteria object or a query builder with zero glue code.

Reuse in a base controller / a filter service

Inject the bundle service into a BaseController and expose a small helper ($this->filter($request, $spec)). That localizes the bundle dependency to one file — critical, given the maintenance status — and makes it trivial to swap later.

Testing

Functional tests are the cheapest way to pin behavior: hit the endpoint with ?page=0, ?limit=abc, ?sort=password_hash and assert 400/403. Because the bundle does casting, also assert the type reaching your service (mock the repository and inspect arguments) — mixed "25" versus 25 bugs are the classic failure here.

Gotchas and Tips

  • Symfony version ceiling. Last release was 2016; it predates Symfony 4 Flex, autowiring defaults, and bundles.php. On modern Symfony expect to either register it manually (config/bundles.php) or, more realistically, treat the README as a spec and reimplement the declarative filter with symfony/validator + a DTO. Budget for this before committing.
  • Everything is a string on the wire. ?active=false casts to a truthy value under naive rules; ?limit= (empty) is not the same as absent. Confirm the bundle's caster behavior for "", "0", "false", "null" in the tests before trusting it with booleans.
  • Array syntax bypasses simple filters. ?limit[]=1&limit[]=2 arrives as an array. If your type is int, make sure the filter rejects or coerces it rather than leaking an array into your query builder.
  • Defaults vs. required. A parameter with a default and required: true is contradictory — pick one. Missing-parameter errors should map to 400, not 500; verify the bundle throws a catchable validation exception you can convert in a kernel exception listener.
  • Internal parameters. Symfony injects _locale, _format, _route into query attributes in some setups. Never feed the whole $request->query->all() through a filter that whitelists nothing — always declare an explicit allowlist, both for safety and for predictable behavior.
  • Don't forget the cache key. If you cache responses, the filtered/typed params must be part of the cache key — otherwise ?page=2 can serve page 1's body. Build the key from the normalized output, not the raw query string.
  • Debugging. dump() the filter output immediately; enable the profiler and inspect the request panel to see what arrived versus what survived. When a constraint unexpectedly passes, check you're using the Symfony constraint name the bundle expects (the README's constraint syntax is a shallow wrapper, not a reimplementation).
  • Extension points. Casting is usually the most useful place to extend — a custom type for DateTime, comma-separated lists, or enum-like values. Look for a caster/type registry in the bundle config; if it's hardcoded, that's your signal to fork.
  • Maintenance risk. Five stars, zero dependents, no release since 2016. Pin the version, isolate the dependency behind your own helper, and keep the README examples in your test suite so you can migrate to a maintained alternative (plain DTO + Validator, or a current query-filter library) without surprises.
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.
elmogy/larafast
xeviant/laravel-paystack
gorilladash/laravel-website-sdk
martialbe/flysystem-oss
chondal/3312client
jeroen-g/flickr
opscale-co/nova-genbi
itsjustvita/laravel-retention
tequilarapido/trackit
gregsphotography/site-builder-filament
cabaoctavio/laravel-query-log
gregsphotography/site-builder-components
io-digital/clickatell
snowsoft/laravel-admin
oxygen/auth
relicsoftware/barakah-payment-gateway
tobya/work-with-composer
equipc/laravel-email-validator
equipc/laravel-email-database-log
adedsec/nova-translated-labels