laravie/query-filter
Laravie Query Filter adds a clean, reusable way to filter Eloquent queries from request input. Define filter classes and apply them to models to handle searching, sorting, and conditional constraints without cluttering controllers or repositories.
Installation
composer require laravie/query-filter
Publish the config (if needed):
php artisan vendor:publish --provider="Laravie\QueryFilter\QueryFilterServiceProvider"
Basic Usage
Use the Filterable trait on your Eloquent model:
use Laravie\QueryFilter\Filterable;
class User extends Model
{
use Filterable;
}
Define Filters
Add a filters() method to your model:
public function filters()
{
return [
'name' => ['like', 'contains'],
'email' => ['equals', 'not_equals'],
'status' => ['in', 'not_in'],
];
}
Apply Filters in a Query
$users = User::filter(request()->all())->get();
Dynamic Filtering from Request
$query = User::filter(request()->query('filters'));
Predefined Filter Sets
$query = User::filter(['status' => 'active', 'role' => 'admin']);
Combining with Other Query Methods
$query = User::where('active', true)
->filter(request()->query('filters'))
->orderBy('name');
Custom Filter Logic
Override applyFilters() in your model:
protected function applyFilters($query, array $filters)
{
if (isset($filters['custom'])) {
$query->where('created_at', '>', now()->subDays(7));
}
return parent::applyFilters($query, $filters);
}
API Resource Filtering
// In a controller
$users = User::filter(request()->query('filters'))->get();
return new UserResource($users);
Form Request Validation
Validate filter inputs in a FormRequest:
public function rules()
{
return [
'filters.name' => 'sometimes|string',
'filters.status' => 'sometimes|in:active,inactive',
];
}
API Documentation Document expected filter formats in your API specs (e.g., Swagger/OpenAPI).
Testing
Test filter logic with assertDatabaseHas or assertSoftDeleted:
$response = $this->get('/users?filters[status]=active');
$response->assertOk();
Case Sensitivity in like/contains
Use LOWER() or ILIKE for case-insensitive searches:
public function filters()
{
return [
'name' => ['like' => 'ILIKE'],
];
}
Nested Filter Arrays Flatten nested arrays in your request:
// Instead of:
// filters[user][status]=active
// Use:
// filters[status]=active
Performance with Large Datasets
Avoid unbounded like queries. Add constraints:
public function filters()
{
return [
'name' => ['like' => ['constraint' => 'starts_with']],
];
}
Reserved Keywords
Escape column names if they match SQL keywords (e.g., order):
public function filters()
{
return [
'`order`' => ['equals'],
];
}
Log Filter Queries
Add a toSql() debug line:
$query = User::filter(request()->query('filters'));
\Log::debug($query->toSql(), $query->getBindings());
Check Filter Definitions
Ensure filters() returns an array of valid operations (e.g., ['equals', 'not_equals']).
Validate Inputs
Use request()->validate() or a FormRequest to catch malformed filters early.
Custom Filter Operations Extend the package by adding new operations in a service provider:
QueryFilter::extend('custom', function ($query, $value) {
return $query->where('column', '>', $value);
});
Global Filter Middleware Apply filters globally in a middleware:
public function handle($request, Closure $next)
{
if ($request->has('filters')) {
$model = app($request->route('model'));
$model::filter($request->query('filters'));
}
return $next($request);
}
Filter Groups Support multi-tenancy or role-based filters:
public function filters()
{
return [
'tenant_id' => ['equals' => auth()->user()->tenant_id],
'role' => ['in' => auth()->user()->allowed_roles],
];
}
Caching Filtered Queries Cache filtered results with tags:
$users = Cache::tags(['users', 'status:active'])->remember(
'filtered_users',
now()->addHours(1),
fn() => User::filter(['status' => 'active'])->get()
);
New Framework Features Leverage Laravel 11’s improved dependency injection and container features for cleaner integration:
// Example: Using Laravel 11's new `app()` helper
$filteredQuery = app(User::class)->filter(request()->query('filters'));
Model Binding Updates Ensure your routes and controllers are updated to use Laravel 11’s model binding syntax if applicable:
// In routes/api.php
Route::get('/users/{user}', function (User $user) {
return User::filter(request()->query('filters'))->get();
});
Testing with Laravel 11 Update your tests to use Laravel 11’s testing helpers:
public function test_filtering_users()
{
$response = $this->get('/users?filters[status]=active');
$response->assertOk();
// Use Laravel 11's assertion methods
}
How can I help you explore Laravel packages today?