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

Eloquent Api Filter Laravel Package

matthenning/eloquent-api-filter

A Laravel package for turning request query parameters into Eloquent API filters, helping keep controllers clean and expose flexible, readable filtering for API endpoints with minimal configuration.

View on GitHub
Deep Wiki
Context7

Getting Started

This is a small, single-purpose package: it turns query-string parameters into Eloquent where clauses so you do not hand-write a filter method for every index endpoint. Treat it as a thin convenience layer, not a full query-builder abstraction.

Minimal setup:

composer require matthenning/eloquent-api-filter

Then, in the model you want to expose:

use Matthenning\EloquentApiFilter\Traits\Filterable; // verify exact namespace in vendor/

class User extends Model
{
    use Filterable;
}

And in the controller:

public function index(Request $request)
{
    return User::query()
        ->filter($request->query())
        ->paginate();
}

The exact trait/scope names should be confirmed against the package README and vendor/matthenning/eloquent-api-filter/src before wiring it into a real endpoint. Where to look first:

  1. README.md for the supported query-string syntax.
  2. src/ for the trait, service provider, and any config file.
  3. config/ after publishing, if the package exposes one:
    php artisan vendor:publish --provider="Matthenning\EloquentApiFilter\ServiceProvider"
    

First practical use case: an internal admin table or prototype API where clients need ad-hoc filtering:

GET /api/users?filter[name]=Ada&filter[active]=1
GET /api/orders?filter[status]=paid&filter[created_at][gte]=2024-01-01

Start with one model and one endpoint. Confirm the generated SQL with ->toSql() before exposing it to clients.

Implementation Patterns

Controller-level filtering with an allowlist. Do not pass the entire request blindly. Even if the package supports it, validate and constrain which columns can be filtered:

public function index(Request $request)
{
    $request->validate([
        'filter' => ['array'],
        'filter.name' => ['string', 'max:255'],
        'filter.active' => ['boolean'],
    ]);

    return User::query()
        ->filter($request->only('filter'))
        ->paginate(25);
}

If the package supports per-model allowed filters, define them on the model rather than in every controller. That keeps the contract close to the schema.

Compose with existing query scopes. Apply authorization and tenancy scopes first, then filtering, then pagination:

return Order::query()
    ->whereBelongsTo($request->user()->team)
    ->filter($request->query('filter', []))
    ->latest()
    ->paginate();

This ordering matters: filtering should never be able to escape a tenant or policy scope.

FormRequest for filter validation. Put allowed filter keys and value types in a FilterUsersRequest. This is also the right place to normalize booleans and date strings before they reach the database.

Pair with sorting and pagination, not replace them. Keep sorting explicit and allowlisted:

$query = User::query()->filter($request->query('filter', []));

$query->when($request->sort, function ($q, $sort) {
    $q->orderBy(in_array($sort, ['name', 'created_at']) ? $sort : 'created_at');
});

return $query->paginate();

Use it for read-only index endpoints. The package is designed around filtering. Keep writes, bulk exports, and reporting queries on explicit, typed query objects where you control the SQL shape.

Custom filter extension points. If the package exposes custom filter classes or a resolver callback, use those for domain-specific filters such as filter[search]=... or filter[overdue]=1, rather than stuffing raw column filters into the URL.

Feature-test the endpoint contract. Assert that a filter changes the result set and that a disallowed filter is ignored or rejected:

$this->getJson('/api/users?filter[active]=1')
    ->assertOk()
    ->assertJsonCount(3, 'data');

Gotchas and Tips

  • No automatic column allowlist is a security risk. A URL-driven filter can expose columns you did not intend to query, or produce SQL errors on invalid columns. Always constrain filter keys in a FormRequest or model configuration.
  • Type juggling bites. Query strings are strings: "0", "false", and "" are easy to mishandle. Cast dates, booleans, and integers explicitly before filtering.
  • Nested filters and arrays need validation. filter[created_at][gte] and filter[status][]=paid can arrive in unexpected shapes. Validate the array structure, not just the top-level filter key.
  • Relationship filters may join or use subqueries. Check whether filtering on a related column causes duplicate rows. Use distinct() or whereHas() if the generated SQL joins.
  • Conflicts with other query packages. If you already use Spatie Query Builder, Laravel Query Builder, or a custom filter scope, namespace collisions and double-filtering are likely. Pick one approach per endpoint.
  • GPL-3.0 license. This is copyleft. For a server-side PHP dependency that is not distributed, the practical risk is usually low, but get legal review if you ship the application to customers or have strict OSS policies.
  • Low adoption and small maintenance surface. With roughly 10 stars and a last release in January 2024, expect to read the source and be prepared to fork or replace it. Pin the version in composer.json and avoid relying on undocumented behavior.
  • Debug by inspecting SQL. Use ->toSql() and ->getBindings() or Laravel Telescope to confirm what the package actually generates. Do not assume the query-string syntax maps one-to-one to where clauses.
  • Extension points are your escape hatch. If the package exposes config, custom filters, or overridable methods, prefer them over patching vendor code. Wrap the package behind a small application-level query object so you can swap it later.
  • Keep the public contract narrow. Expose only the filters each endpoint needs. A generic “filter anything” endpoint is convenient in prototypes and painful to secure and document in production.
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