Installation:
composer require tucker-eric/eloquentfilter
Add to config/app.php under aliases:
'EloquentFilter' => TuckerEric\EloquentFilter\Filter::class,
First Use Case:
Define a filterable model (e.g., User) by extending TuckerEric\EloquentFilter\Filterable:
use TuckerEric\EloquentFilter\Filterable;
class User extends Authenticatable
{
use Filterable;
}
Basic Filtering: In a controller, inject the filter and apply it to a query:
use TuckerEric\EloquentFilter\Filter;
public function index(Request $request, Filter $filter)
{
$users = $filter->apply(User::query(), $request->all());
return $users->get();
}
Dynamic Filtering:
Use apply() to dynamically filter models based on request input:
$filtered = $filter->apply(
Model::query(),
$request->input('filters', [])
);
Custom Filter Rules:
Define rules in a filterable array in your model:
protected $filterable = [
'name', 'email',
'roles' => ['whereIn', 'role_id'],
'active' => ['scope', 'isActive']
];
Relationship Filtering:
Filter nested relationships (e.g., posts with author_id):
protected $filterable = [
'posts.author_id' => ['where', 'author_id']
];
API Integration: Use with Laravel API Resources for filtered responses:
return UserResource::collection($filter->apply(User::query(), $request->all()));
Form Requests:
Validate and pass filtered input from a FormRequest:
public function rules()
{
return [
'name' => 'sometimes|string',
'roles.*' => 'sometimes|integer',
];
}
public function filter(Request $request, Filter $filter)
{
return $filter->apply(User::query(), $this->validated());
}
Caching: Cache filtered queries for performance:
$cacheKey = md5(serialize($request->all()));
return Cache::remember($cacheKey, now()->addHours(1), function () use ($filter, $request) {
return $filter->apply(User::query(), $request->all())->get();
});
Pagination: Combine with Laravel pagination:
$filtered = $filter->apply(User::query(), $request->all());
return $filtered->paginate($request->input('per_page', 15));
Search: Use like for partial matches:
protected $filterable = [
'search' => ['where', 'name', 'like', '%{$search}%']
];
SQL Injection:
Always validate/sanitize input before passing to apply(). Use FormRequest or manual validation.
Relationship Ambiguity:
Avoid duplicate relationship keys (e.g., posts.author_id and posts.title). Clarify with unique prefixes:
'posts.author_id' => ['where', 'author_id'],
'posts.title' => ['where', 'title']
Empty Arrays:
Explicitly handle empty arrays in filters to avoid whereIn issues:
if (!empty($request->roles)) {
$filter->apply(User::query(), $request->all());
}
Case Sensitivity:
Database collations affect like/where filters. Use LOWER() in rules if needed:
'name' => ['where', 'LOWER(name)', 'like', 'LOWER(%{$name}%)']
Log Queries: Enable Laravel query logging to inspect generated SQL:
DB::enableQueryLog();
$filter->apply(User::query(), $request->all());
dd(DB::getQueryLog());
Test Rules: Validate rules in isolation:
$filter = new Filter();
$query = User::query();
$filter->apply($query, ['name' => 'test']);
dd($query->toSql());
Custom Operators:
Extend the filter with new operators (e.g., between):
$filterable = [
'created_at' => ['between', 'created_at', '2020-01-01', '2020-12-31']
];
Macros: Add reusable filter logic via query macros:
User::macro('filterByRole', function ($roleId) {
return $this->whereHas('roles', fn($q) => $q->where('id', $roleId));
});
Middleware: Pre-process requests in middleware:
public function handle($request, Closure $next)
{
$request->merge(['filters' => $this->transformFilters($request->all())]);
return $next($request);
}
Events:
Listen for filter events (e.g., Filtering) to log or modify queries:
Event::listen('Filtering', function ($query, $filters) {
// Custom logic
});
Default Rules: Override default rules in config/eloquentfilter.php:
'default_rules' => [
'like' => ['where', '{field}', 'like', '%{value}%'],
],
Strict Mode: Enable to throw exceptions on invalid rules:
$filter = new Filter(['strict' => true]);
How can I help you explore Laravel packages today?