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 Filter Laravel Package

coliving/eloquent-filter

A Laravel package for building clean, reusable Eloquent query filters. Encapsulate where clauses and request-driven filtering into dedicated filter classes, keeping controllers and models tidy and your queries composable.

View on GitHub
Deep Wiki
Context7

Getting Started

1. Install and confirm what you actually got. The package name is coliving/eloquent-filter, but the repo lives under dungnh/eloquent-filter — vendor/namespace mismatches are common in small packages, so verify both before assuming a typo:

composer require coliving/eloquent-filter
composer show coliving/eloquent-filter   # confirm autoload namespace + version

Read the package's composer.json and src/ directory first, not just the README. With 0 stars and 0 dependents, the README is likely thin, and the source is the source of truth for method names and trait/interface contracts.

2. Wire the trait into the model you want filterable. The typical shape for this family of packages is a trait that adds a filter()/scopeFilter() entry point plus a whitelist of allowed filters:

use Coliving\EloquentFilter\Traits\Filterable; // verify exact namespace

class Product extends Model
{
    use Filterable;

    protected $filterable = ['name', 'status', 'price_min', 'price_max'];
}

3. Define your first filter and call it from a controller. The first real use case is always an index endpoint — GET /products?name=shoe&status=active:

public function index(Request $request)
{
    return Product::query()
        ->filter($request->query())   // or ->filter($request->only(...))
        ->paginate(25);
}

Before writing more, throw a request with an unexpected key (?foo=bar) at it and see what happens. That single test tells you whether the package silently ignores unknown keys or tries to resolve them dynamically — which dictates everything in the "Gotchas" section below.

Implementation Patterns

Whitelist at the edge, not in the model. Even if the model declares $filterable, pass only validated input from the controller. A FormRequest with sometimes|string rules per filter key gives you type safety, documentation, and protection against malformed values in one place:

$filters = $request->validated();       // only known, typed keys reach the package
return Product::filter($filters)->paginate(25);

One method per filter, named predictably. Most implementations of this pattern resolve filterName to a method like filterByName() or scopeFilterName(). Keep them small and composable so each is independently testable:

public function filterByStatus($query, $value)
{
    return $query->where('status', $value);
}

public function filterByName($query, $value)
{
    return $query->where('name', 'like', "%{$value}%");
}

public function filterByPriceMin($query, $value)
{
    return $query->where('price', '>=', (float) $value);
}

Split logic into a dedicated filter class once the model grows. If the package supports a standalone filter class (many do), prefer it over stuffing the model — a ProductFilter class keeps a 40-method model from becoming unreadable and lets you inject dependencies (current user, tenant ID):

class ProductFilter extends BaseFilter
{
    public function filterByOwner($query, $value)
    {
        return $query->where('user_id', auth()->id());
    }
}

Compose with the rest of the query builder. Filters are just query-builder mutations — chain with(), withCount(), orderBy(), and paginate() around them. This is the package's real value: it keeps controllers scoped-looking without a switch statement per parameter.

Centralize sorting and pagination alongside filters. Query-string-driven APIs almost always grow a ?sort= and ?per_page=. Handle those yourself with an explicit allow-list map; don't expect the filter package to cover them, and don't let a raw ?sort=column reach orderBy().

Use it for admin/back-office indexes first. The pattern shines where the query surface is large and controlled (internal dashboards, exports, reporting). For a public API with three parameters, when() clauses in a local scope are clearer and have zero dependency risk.

Gotchas and Tips

Confirm the dynamic-resolution behavior before shipping. If the package maps query keys to methods reflectively, an unprotected endpoint can invoke arbitrary methods on the model or filter class. Always gate with the whitelist and pass $request->validated() — never $request->all() or $request->query() directly in production code.

empty() and "0". Filters that early-return on empty values frequently swallow ?status=0 or ?price_min=0, because PHP's empty("0") is true. If numeric-zero filters matter to you, check the source and patch/test accordingly.

Type juggling on booleans and ranges. ?active=false arrives as the string "false", which is truthy in PHP. Cast explicitly in your filter methods (filter_var($value, FILTER_VALIDATE_BOOLEAN)) rather than trusting the package to do it.

Laravel version support is the biggest adoption risk. Last release was 2022-04-05, which likely targets Laravel 8/9. Laravel 10/11 tightened method signatures, changed Model contracts, and bumped PHP minimums — expect deprecation warnings or hard breaks. Pin the version, run the full test suite, and specifically exercise a filtered paginated query on your target Laravel version before merging.

Zero stars, zero dependents, no recent commits. Treat this as an unmaintained package you are effectively adopting. Mitigations, in order of preference: (a) implement the 30-line trait yourself, (b) fork it into your own app/Support namespace, (c) require it with a caret-pinned version and a comment in composer.json explaining why. For a package this small, option (a) is usually cheaper than the maintenance overhead — the value here is the pattern, not the code.

Watch for N+1 and unbounded whereHas. Relationship filters (filterByCategory) will generate whereHas subqueries; on large tables these can be dramatically slower than a join. Run DB::listen() or Laravel Debugbar against realistic data before trusting it in a listing endpoint.

Debugging: inspect the SQL, not the result. After applying filters, ->toSql() with ->getBindings(), or ->dd() mid-chain, is the fastest way to see whether a filter silently did nothing. "Empty result set" from this class of package is almost always a filter that returned early or a key that never matched a method.

Test the applied filter set, not the SQL string. Assert on returned IDs and on the count of results for each parameter combination. SQL-string assertions break the moment the package changes a where clause ordering.

Extension points worth knowing. Look for a base filter class or config file — overriding the default filter resolution there is far cleaner than editing vendor code. If none exists, wrap the trait's entry point in your own scopeFiltered() and route all callers through it; that single indirection makes swapping the package out later a one-file change.

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.
abbasudo/artisan-route
mirvan/ippanel
ignibyte/aic-framework
glavweb/data-schema-bundle
glavweb/datagrid-bundle
tacticmedia/scim-server
uni-method/frame
neika-ai/sdk
adachsoft/php-code-style
pharaonic/laravel-notification-dependencies
beautinow/translug
dev-brainiac/bot-cloaked
octoberrain/html
octoberrain/extension
octoberrain/exception
octoberrain/contracts
adachsoft/api-version-bundle
cgv/localized-resource
noith/settings
kda/laravel-package