leocarmo/laravel-eloquent-model-filter
A Laravel package for adding declarative filters to Eloquent models. Define allowed filters once, apply them from request query strings, and keep complex query logic reusable, readable, and easy to test across your application.
Architecture fit
The package targets the read path: a model-level trait plus a per-model filter class that translates request query parameters into Eloquent where clauses. That maps cleanly onto a conventional Laravel layering (Controller → Service/Query Object → Eloquent) and is the same shape as the widely adopted tucker-eric/eloquentfilter pattern — this package appears to be an opinionated derivative of it. Architecturally it is a cross-cutting concern bolted onto the domain model, which is acceptable in Laravel but does mean persistence-layer code inherits HTTP-shaped input. For teams already using query objects or repository patterns, the value is marginal; for teams with fat controllers, it removes a real class of duplication.
Integration feasibility
Low as a direct Composer dependency. The 2018-08-21 release predates Laravel 6 and PHP 8; expect constraints around illuminate/*: 5.5–5.7 and php: ^7.1. On Laravel 9+ Composer will refuse resolution without --ignore-platform-reqs, which is not an acceptable production posture (it disables the only safety net you have against transitive incompatibility). Feasibility is moderate-to-high as vendored source — the trait itself is likely 100–200 lines of where/whereLike/whereIn dispatch and is trivially portable.
| Dimension | Assessment | Confidence |
|---|---|---|
| Maintenance status | Abandoned; single author, no release in ~7 years | High |
| Ecosystem validation | 11 stars, 0 dependents — effectively zero production signal | High |
| Laravel/PHP compatibility | Legacy only; unsafe on Laravel 9+ / PHP 8.2+ | High |
| Code surface / auditability | Small; fully auditable in an afternoon | Medium |
| Security posture | No disclosure process, no patched version path | High |
| Reversibility | High — trait is easy to strip out | High |
Technical risk
$filterable or equivalent) is optional or permissive, you inherit an injection / information-disclosure surface (filtering on password_hash, enumerating via error-based probing). This must be verified before any exposure to untrusted traffic.whereBelongsTo, relationship aggregates, chaperone(), or strict-mode behaviors added in later Laravel versions; nested/relationship filtering is likely naive.Key questions for engineering
composer.json constraints, and does it install clean on our current Laravel/PHP?laravel-query-builder, tucker-eric/eloquentfilter) that covers 90% of the need?Recommendation: treat this as a reference implementation, not a dependency. Budget 1–2 engineer-days to reimplement the pattern in-house behind an explicit allowlist; that is cheaper than the indefinite carrying cost of a fork.
Stack fit Directly viable only on a legacy Laravel 5.5–5.8 / PHP 7.x service. On Laravel 8+ the package is not a stack fit — the integration becomes "adopt the pattern." If we are on 5.x and the service is slated for upgrade within two quarters, installing now creates a migration liability we will pay for twice.
Compatibility matrix (to be verified, not assumed)
| Our runtime | Direct install | Recommended path |
|---|---|---|
| Laravel 5.5–5.7 / PHP 7.1–7.4 | Plausible | Install + allowlist hardening |
| Laravel 8–9 / PHP 8.0–8.1 | Unlikely without ignores | Vendor trait into app/Support/Filtering |
| Laravel 10–12 / PHP 8.2+ | No | Reimplement or Spatie laravel-query-builder |
Migration path
null, empty string, comma-lists, ? placeholders).app/Support/Filtering under our namespace, with a header comment attributing origin and license.['status' => 'order_status']) so request keys never touch the query builder directly; add a hard cap on filter count and reject unknown keys.laravel-query-builder, which is actively maintained and gives the same ergonomics with a documented security model.Sequencing Do the audit (half day) before committing to either path — the answer to "how big is the dispatch method" determines fork vs. reimplement. Harden and test in the same PR as the vendor-in; never ship the trait and the allowlist as separate changes. Schedule the replacement evaluation only if filtering requirements expand past scalar equality.
Maintenance
Bus factor of one, and that one has not shipped since 2018. Owning this means owning a fork: tracking upstream Laravel breaking changes (query builder signatures, whereLike semantics, strict-mode default flips) with no upstream to lean on. Estimate ~2–4 hours per major Laravel upgrade to re-validate a vendored copy — acceptable, but it must be an explicit line item somewhere, not ambient hope.
Support No SLA, no security disclosure channel, no maintainer responsiveness. With 11 stars and 0 dependents there is effectively no community to search for answers — every question is answered by reading the source. Onboarding cost is genuinely low (an afternoon), which is also the trap: the low ramp-up disguises the absence of a safety net.
Scaling
The filter layer itself is negligible — trait resolution and a handful of where calls per request. The scaling risk is behavioral, not computational: unconstrained filtering invites consumers to build arbitrary queries, which produces unindexed LIKE '%term%' scans, deep offset pagination, and filters on high-cardinality columns. Pair any adoption with per-endpoint query budgets, required pagination limits, and index review for every newly allowlisted column.
Failure modes
Ramp-up and cost Engineer ramp-up: half a day. Reimplementation: 1–2 days including tests. Ongoing fork carrying cost: low but perpetual. Against that, the package saves perhaps a day of initial development — which is why the honest TPM conclusion is that the economics favor reimplementing the 150-line pattern and spending the saved risk budget on the allowlist hardening and test suite that this package does not ship.
How can I help you explore Laravel packages today?