Installation
composer require cannibal/filter-bundle
Add to config/app.php under providers:
Cannibal\FilterBundle\FilterBundle::class,
Publish the config (if needed):
php artisan vendor:publish --provider="Cannibal\FilterBundle\FilterBundle"
Basic Usage
Define a filterable resource (e.g., User model) with a Filterable trait or interface:
use Cannibal\FilterBundle\Contracts\Filterable;
class User implements Filterable
{
// ...
}
First Filter Request
Add filters to a request (e.g., GET /users?filter[name]=John&filter[active]=true).
The bundle automatically parses and applies them via a FilterManager.
Use annotations or YAML/XML to define filter rules for a resource:
# config/filters/user.yaml
filters:
name:
type: string
operator: contains
active:
type: boolean
operator: eq
created_at:
type: date
operator: between
Leverage the FilterManager to process requests:
use Cannibal\FilterBundle\FilterManager;
class UserController extends Controller
{
public function index(FilterManager $filterManager)
{
$users = $filterManager->apply(
User::query(),
request()->all()
);
return $users;
}
}
Extend the bundle by creating custom operators:
use Cannibal\FilterBundle\Contracts\FilterOperator;
class CustomOperator implements FilterOperator
{
public function apply($query, $field, $value)
{
return $query->where($field, 'like', "%{$value}%");
}
}
Register in config/filters.php:
'operators' => [
'custom' => \App\Filters\CustomOperator::class,
],
Use the FilterRequest middleware to parse and validate filters:
use Cannibal\FilterBundle\Http\Middleware\FilterRequest;
$router->middleware(FilterRequest::class);
For dynamic collections (e.g., Eloquent queries), inject the FilterManager into services:
class UserService
{
public function __construct(private FilterManager $filterManager) {}
public function getFilteredUsers(array $filters)
{
return $this->filterManager->apply(
User::query(),
$filters
);
}
}
Naming Conventions
filter[field]=value format in requests. Deviations (e.g., ?name=John) won’t work unless customized.Query Builder Compatibility
between) may require raw SQL or custom implementations for non-Eloquent queries.Caching Issues
Type Safety
boolean, date). Invalid types (e.g., "true" for a boolean field) may cause errors.request()->all()['filter'] ?? [];
config/filters.php includes all required fields. Missing definitions will silently ignore filters.FilterManager to support non-request sources (e.g., GraphQL inputs):
$manager->apply(User::query(), $graphqlInput['filters']);
FilterManager to reorder or skip operators dynamically.contains, between) by overriding the FilterBuilder.cursor() or chunk() after filtering to avoid memory issues.->get() over ->all() when filters are applied to collections.$manager->shouldReceive('apply')
->once()
->andReturn(User::factory()->count(2));
How can I help you explore Laravel packages today?