laravel-query-filter maintained by oooiik
Laravel Query Filter
A clean, convention-based way to extract Eloquent query filters into dedicated filter classes. Keep your controllers thin, your scopes focused, and your filtering logic testable.
// Before — filtering logic leaking into the controller
$users = User::query()
->when($request->username, fn($q, $v) => $q->where('username', $v))
->when($request->role, fn($q, $v) => $q->whereHas('role', fn($r) => $r->where('title', $v)))
->when($request->created_after, fn($q, $v) => $q->where('created_at', '>=', $v))
->paginate();
// After — one line, all filtering in UserFilter
$users = User::filter($request->validated())->paginate();
Features
- 🎯 Convention over configuration — each public method on your filter class becomes a filter key. No registration, no metadata.
- 🪶 Single trait + base class — add
Filterableto a model, point it at a filter class, done. - 🛠 Artisan generator —
php artisan make:filter UserFilterscaffolds the class for you. - 🔁 Composable — apply multiple parameter sets to the same filter instance and chain into the query.
- ⚙️ Defaults & fallbacks — provide default parameter values and fallback handlers for missing keys.
- 🧩 Laravel 6 → 12 — broad compatibility, PHP 7.3+ through 8.x.
Installation
composer require oooiik/laravel-query-filter
The service provider is auto-registered via Laravel's package discovery.
Quick Start
1. Generate a filter
php artisan make:filter UserFilter
This creates app/Filters/UserFilter.php.
2. Define your filter methods
Each public method becomes a filter key matching its name:
namespace App\Filters;
use Oooiik\LaravelQueryFilter\Filters\QueryFilter;
class UserFilter extends QueryFilter
{
public function username($username)
{
$this->builder->where('username', $username);
}
public function role($role)
{
$this->builder->whereHas('role', function ($query) use ($role) {
$query->where('title', $role);
});
}
public function createdAfter($date)
{
$this->builder->where('created_at', '>=', $date);
}
}
3. Attach the filter to your model
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
use Oooiik\LaravelQueryFilter\Traits\Model\Filterable;
use App\Filters\UserFilter;
class User extends Model
{
use Filterable;
protected $defaultFilter = UserFilter::class;
}
4. Use it
// Controller
public function index(Request $request)
{
$validated = $request->validate([
'username' => 'nullable|string',
'role' => 'nullable|string',
'createdAfter' => 'nullable|date',
]);
return User::filter($validated)->paginate();
}
Missing keys are silently ignored — only the filter methods that match input parameters run.
Advanced Usage
Default parameters
Use the $default property to pre-fill values when a key is missing from the input:
class UserFilter extends QueryFilter
{
public $default = [
'status' => 'active',
'sort' => 'created_at',
];
public function status($status)
{
$this->builder->where('status', $status);
}
public function sort($column)
{
$this->builder->orderBy($column, 'desc');
}
}
Calling User::filter([]) will still apply status = active and sort by created_at desc.
Fallback methods
Use $fallback to redirect missing input keys to a different method:
class UserFilter extends QueryFilter
{
public $fallback = [
'search' => 'searchByName',
];
public function searchByName($value)
{
$this->builder->where('name', 'like', "%{$value}%");
}
}
If the search key is missing from input, searchByName runs with whatever value was provided as the fallback source.
Standalone filter instance (chaining)
Apply multiple parameter sets to the same filter:
$filter = User::createFilter(UserFilter::class);
$filter->apply(['role' => 'admin']);
$filter->apply(['status' => 'active']);
$query = $filter->query();
// Both filter sets are now applied to the builder
Accessing all parameters
Filter methods receive the full parameter array as a second argument:
public function username($username, $allParams)
{
if (! empty($allParams['exact_match'])) {
$this->builder->where('username', $username);
} else {
$this->builder->where('username', 'like', "%{$username}%");
}
}
Comparison with spatie/laravel-query-builder
laravel-query-filter |
spatie/laravel-query-builder |
|
|---|---|---|
| Approach | Convention-based — method = filter key | Declarative — register allowed filters explicitly |
| Per-model class | Yes, dedicated filter class | Optional, often inline |
| Custom filter logic | Plain PHP method | AllowedFilter::callback() |
| Best for | Complex filtering with reusable logic | API endpoints with simple filtering needs |
Both are great — choose laravel-query-filter when you want a dedicated class per model with reusable, testable filter logic.
Requirements
- PHP 7.3 or higher
- Laravel 6.x — 12.x
Compatibility Matrix
| Laravel | PHP | Status |
|---|---|---|
| 12.x | 8.2+ | ✅ Supported |
| 11.x | 8.2+ | ✅ Supported |
| 10.x | 8.1+ | ✅ Supported |
| 9.x | 8.0+ | ✅ Supported |
| 8.x | 7.3+ | ✅ Supported |
| 7.x | 7.3+ | ✅ Supported |
| 6.x | 7.3+ | ✅ Supported |
Contributing
Pull requests are welcome. For substantial changes, please open an issue first to discuss the direction.
Bug reports and feature ideas → GitHub Issues.
Credits
- Obidjon Toshev — author & maintainer
- All contributors
License
The MIT License (MIT). See LICENSE for details.