isma/api-filters-bundle
A Laravel package for clean, query-string-driven API filtering. Turn request parameters into Eloquent queries with reusable filters, sorting, and pagination—keeping controllers thin and endpoints consistent. Simple to configure, easy to extend, and built for real-world REST APIs.
Architecture fit
if ($request->has('status')) chains accumulating in controllers.symfony/http-kernel, symfony/dependency-injection, and symfony/config, and is activated through AppKernel/bundles.php registration plus a Configuration tree. Laravel has no equivalent activation path, so the bundle's wiring layer remains inert under Laravel even if Composer installs it cleanly. Only the concept and possibly some framework-agnostic classes are portable.QueryBuilder (andWhere, setParameter, expr()), they are not usable against Eloquent's Builder. v1.3.0 gives us indirect evidence that the coupling is Doctrine-flavored rather than abstract: ilike / ieq / inoteq are operator names that map almost one-to-one onto PostgreSQL semantics and onto Doctrine's Comparison/expression helpers. If the core were builder-agnostic, we would expect a documented operator-enum extension point instead of hardcoded additions. Still an inference, not a confirmed fact — but it shifts the prior toward Doctrine-specific.Integration feasibility
ilike is native PostgreSQL. On MySQL it must be emulated with LOWER(col) LIKE LOWER(?), which is a function call on the column and therefore non-sargable — it defeats the index. ieq has the same problem, though it is usually benign because equality on a small-cardinality indexed column is cheap regardless. inoteq (case-insensitive not-equal) is worst-case: it is a negation, which is inherently low-selectivity and now also non-indexable. A Laravel target on MySQL will silently inherit a performance cliff if these operators are exposed without review, and nothing in the release notes suggests dialect awareness was considered.2026-03-19 anomaly) should still be reconciled against the actual tag history, though the existence of a coherent v1.2.1 → v1.3.0 compare link is a mild positive signal on repository hygiene.Technical risk
inoteq negation operator is the specific concern: negations force the planner away from index scans on large tables, making it the cheapest possible client-side lever for a costly query. Verify the allowlist before trusting the request-to-column mapping — the question is now more urgent than it was.ilike means "case-insensitive match" — but how it means that is database-dependent. If the product runs PostgreSQL in one environment and MySQL in another, the same filter string produces different result sets and different query plans. That is a correctness risk, not just a performance one, and it is invisible until someone compares outputs across environments.?filter[status][eq]=active, comma-separated IN, nested relations, and now ilike/ieq/inoteq) becomes a public API contract the moment a client integrates. Changing it later — or declining to support an operator a client has already adopted — is a breaking change against your own consumers.Key questions
ilike / ieq / inoteq compiled for non-PostgreSQL platforms? Is there dialect detection, a documented MySQL/ SQLite strategy, or an unstated PostgreSQL assumption?Stack fit
laravel-doctrine). Here the query-builder layer may genuinely apply, and only the DI/config wiring needs replacing with a service provider. Still the highest-leverage adoption scenario and still the one to evaluate first.Migration path
Ranked, with the tradeoff stated:
ilike/ieq/inoteq are in scope and, if so, how they compile on the databases you actually run.Compatibility
Request, so a PSR-based resolver needs a thin adapter.?filter[name][ilike]=x and being silently ignored will now get filtered results. Audit whether any existing consumer sends unrecognized operators before bumping.Sequencing
ilike/ieq/inoteq are compiled and whether the operator set is extensible via config or hardcoded. Output: a go/no-go on bridging versus reimplementing, plus a decision on operator scope.Maintenance
Support
Scaling
DISTINCT, which defeats index usage.ilike and ieq are non-sargable on MySQL/SQLite by construction. They wrap the column in LOWER(), so no index is used on that predicate regardless of how well the column is indexed. On a large table this converts an index seek into a full scan. inoteq compounds the problem: a negation is already low-selectivity, and a case-insensitive negation cannot use an index at all. Treat these three operators as opt-in per field, not on by default, and only enable them where the table is small or the column is already in a functional index.ILIKE has the same index limitation unless a pg_trgm GIN index exists. The operator being "native" does not make it fast. Do not conflate "supported" with "indexed."IN lists (client sends 5,000 IDs) and unbounded page sizes remain the classic denial-of-service vectors. Cap both at the policy layer, not in the strategy.Failure modes
ilike was sending garbage. After v1.3.0 the same request returns filtered results. If the upgrade is applied without a consumer audit, previously-ignored parameters silently become active predicates. This is the most likely way v1.3.0 causes a production incident: not a crash, a quietly different result set.?price[gte]=abc reaching the query builder produces a database error that surfaces as a 500 and may leak schema details in the response. Validate and reject at the resolver boundary.LIKE '%...', negations, or case-insensitive scans that force full table reads. The v1.3.0 additions enlarge the set of expensive operators available to an attacker by three.internal_notes or deleted_at is both an information leak and a business-logic bypass. The allowlist must be the default, with explicit opt-in per field.Ramp-up
Bottom line: v1.3.0 is a legitimate, semver-correct feature release from an actively-committing maintainer, and it modestly improves the maintenance-risk picture. It does not change the core recommendation: the design idea is worth adopting, the artifact is not, for a Laravel target. The new operators add a decision the previous assessment did not have to make — whether case-insensitive filtering is in scope at all, and how it compiles on the databases you actually run — and they widen the operator-injection surface enough that the allowlist requirement moves from "verify" to "blocking." Recommend the same two-to-three day spike, now explicitly tasked with determining query-builder coupling and operator extension mechanics, defaulting to reimplementing the strategy pattern as an internal package with a deliberately chosen operator set.
How can I help you explore Laravel packages today?