abbasudo/laravel-purity
Laravel Purity is an elegant filtering and sorting package for Laravel Eloquent. Add filter() to any query and let frontend users filter and sort results through simple URL query string parameters, with no extra boilerplate.
if ($request->has('status')) chains that otherwise get re-written in every controller. PM framing: it converts a recurring 2–5 day per-endpoint chore into a one-line convention, which is the difference between shipping filters for one entity this sprint and shipping them for eight.?filters[title][$contains]=x&sort=-created_at, with operator names ($contains, $eq, $notIn, $between) that frontend, docs, SDKs, and customer integrations will hard-code. This is a roadmap-sequencing decision, not just a library pick: it should be locked before you publish API docs or a public SDK, because changing operators later is a breaking change for customers. Treat it as a versioned interface from day one.$contains LIKE matching against declared columns. It is not full-text search, relevance ranking, faceting with counts, typo tolerance, or typo-tolerant autocomplete. If a roadmap item says "search," decide early whether it means filter the list (Purity) or find the thing (Meilisearch/Typesense/Elastic/Scout). Conflating them is the most common way this adoption gets oversold internally and then disappoints.Adopt when most of these hold:
allowedFilters/ignoredFilters and field renaming are the security-relevant features; they are the reason to prefer this over a bespoke 20-line helper, because a bespoke helper is precisely where someone eventually forgets the allowlist.LIKE/contains and case sensitivity) are reassuring signals — but 0 dependents means you cannot infer production hardening from a downstream ecosystem. Budget for that honestly: read the source, pin the version, and write your own integration tests covering the filters you actually expose.Look elsewhere, or build your own, when:
Adoption gates worth writing into the ticket: pin the version; explicitly enumerate allowed filters and sorts per model (never default to "everything on the model is filterable" — that is how internal columns leak); confirm driver parity for your production database, since contains/case-sensitivity behavior has been fixed per-driver over several releases; add contract tests for the exact filter strings your frontend sends; document the grammar in the API reference in the same PR that adopts it.
To executives (one paragraph). We can add filtering and sorting to our list screens and collection APIs with roughly a one-line change per endpoint instead of custom code per screen. It is an MIT-licensed, widely used Laravel library with no infrastructure, no database migrations, and no vendor relationship — it is a code dependency, not a platform bet, and it can be removed without data loss. The business case is delivery speed and consistency: the same filtering behavior everywhere, fewer tickets per new admin view, and facet experiments that a frontend developer can run without a backend sprint. The decision that does need executive awareness is that this standardizes the URL grammar our API exposes. That is a one-way door for public-facing customers, so we should make that call deliberately and document it before we publish an SDK.
To engineering (the honest version). This is a thin request-to-query translator over Eloquent: no migrations, no config you must publish, no background processes. It composes with existing scopes, global scopes, and policies, so it drops into a typical controller without restructuring. The tradeoff is deliberate and small: the data layer now reads the HTTP query string. That is exactly why Post::filter() is one line, and it is the one thing to check against our architecture conventions before we adopt. Two conditions on adoption: (1) every model gets an explicit allowlist of filterable and sortable fields — the package supports this, and we must not treat "all columns" as the default; (2) we write integration tests against the exact query strings the frontend sends, on the database driver we actually run in production, because operator and case-sensitivity behavior is driver-specific and has been patched several times across releases. We pin the version and own the upgrade.
To the frontend and design partners. Filters and sorts become client-composable: you build the query string, the backend applies it, and adding a facet does not require a backend ticket — provided the field is already on the allowlist, so give us the facet list at design time. Agree on the parameter syntax and operator names early and treat them as an API contract; renaming an operator later breaks saved views and any customer integrations.
What to say no to. Do not sell this internally as "search." It is column-scoped filtering with contains/equality/range operators — no relevance ranking, no faceted counts, no typo tolerance. If the roadmap says "search," scope it separately. Promising search and delivering filters is a cheap way to burn credibility on an otherwise good, boring, correctly-sized dependency.
Risk statement for the decision log. Single-maintainer community package, MIT, ~515 stars, active maintenance including framework compatibility through Laravel 13, but no meaningful downstream dependent base to lean on — so our confidence comes from reading the code and our own test coverage, not from third-party adoption. Mitigation: pinned version, allowlists enforced per model, contract tests, and a documented exit path (the surface we depend on is filter()/sort() plus a field whitelist — small enough to replace with in-house code if maintenance stalls).
How can I help you explore Laravel packages today?