Filterable
🔍 Enhance Laravel queries with adaptable, customisable filters and intelligent caching to improve both performance and functionality.
Install / Use
/learn @Thavarshan/FilterableREADME
About Filterable
Filterable is a Laravel package for turning HTTP request parameters into rich, composable Eloquent query filters. The base Filter class exposes a stateful pipeline that you can extend, toggle, and compose with traits to add validation, caching, logging, rate limiting, memory management, and more. Everything is opt-in, so you enable only the behaviour you need while keeping type-safe, testable filters.
Requirements
- PHP 8.3, 8.4, or 8.5
- Laravel 11.x, 12.x, or 13.x components (
illuminate/cache,illuminate/contracts,illuminate/database,illuminate/http,illuminate/support) - A configured cache store when you enable caching features
- A PSR-3 logger when you enable logging (optional)
Installation & Setup
composer require jerome/filterable
Package auto-discovery registers the FilterableServiceProvider, which contextual-binds the current Request into resolved filters and exposes the make:filter Artisan command. Publish the configuration to set global feature defaults, cache behaviour, or runtime options:
php artisan vendor:publish --tag=filterable-config
Stubs live under src/Filterable/Console/stubs/ and can be overridden by placing copies in your application's stubs directory.
Highlights
- Publishable configuration (
config/filterable.php) to set default feature bundles, runtime options, and cache TTLs that the base filter reads during construction. - Stateful lifecycle with
apply,get,runQuery,reset, rich debug output viagetDebugInfo(), lifecycle events (FilterApplying,FilterApplied,FilterFailed), and configurable exception handling. - Opt-in concerns for validation, permissions, rate limiting, caching (with heuristics), logging, performance metrics, query optimisation, memory management, value transformation, and fluent filter chaining.
- Drop-in
FilterableEloquent scope trait so any model can accept a filter instance. - Smart caching that builds deterministic cache keys, supports tags, memoises counts, and can decide automatically when to cache complex queries.
- Contextual binding in
FilterableServiceProvidermakes sure container-resolved filters receive the current HTTPRequest; injecting a cache repository or PSR-3 logger auto-enables the relevant features. - Memory-friendly helpers (
lazy,stream,streamGenerator,lazyEach,cursor,chunk,map,filter,reduce) when thememoryManagementfeature is enabled. - First-party Artisan generator with
--basic,--model, and--forceoptions to rapidly scaffold filters.
Repository Layout
src/Filterable/Filter.php– abstract base class orchestrating the filter lifecycle and feature toggles.src/Filterable/Concerns/– traits implementing discrete behaviour (filter discovery, validation, caching, logging, performance, optimisation, rate limiting, etc.).src/Filterable/Contracts/– interfaces for the filter pipeline and the Eloquent scope signature.src/Filterable/Traits/Filterable.php– model scope that forwards to aFilterinstance.src/Filterable/Console/MakeFilterCommand.php&src/Filterable/Console/stubs/– Artisan generator and overrideable stub templates.src/Filterable/Providers/FilterableServiceProvider.php– registers the package and console command viaspatie/laravel-package-tools.bin/– executable scripts executed by the Composerlint,fix, andtestcommands.tests/– Orchestra Testbench suite with concern-focused tests and reusable fixtures intests/Fixtures/.assets/– shared media used in documentation.config/filterable.php– publishable defaults for feature toggles, cache TTL, and runtime options.database/factories/– reserved for additional factories should you extend the package.
Quick Start
1. Generate a filter
php artisan make:filter PostFilter --model=Post
--model wires the stub to your Eloquent model. Use --basic for an empty shell or --force to overwrite an existing class.
2. Implement filtering logic
<?php
namespace App\Filters;
use Filterable\Filter;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Http\Request;
use Illuminate\Support\Carbon;
use Illuminate\Validation\Rule;
class PostFilter extends Filter
{
/**
* Request keys that map straight to filter methods.
*
* Methods follow camelCased versions of the keys (e.g. published_after → publishedAfter).
*/
protected array $filters = ['status', 'published_after', 'q'];
public function __construct(Request $request)
{
parent::__construct($request);
$this->enableFeatures([
'validation',
'optimization',
'filterChaining',
'valueTransformation',
]);
$this->setValidationRules([
'status' => ['nullable', Rule::in(['draft', 'published'])],
'published_after' => ['nullable', 'date'],
]);
$this->registerTransformer('published_after', fn ($value) => Carbon::parse($value));
$this->registerPreFilters(fn (Builder $query) => $query->where('is_visible', true));
$this->select(['id', 'title', 'status', 'published_at'])->with('author');
}
protected function status(string $value): void
{
$this->getBuilder()->where('status', $value);
}
protected function publishedAfter(Carbon $date): void
{
$this->getBuilder()->whereDate('published_at', '>=', $date);
}
protected function q(string $term): void
{
$this->getBuilder()->where(function (Builder $query) use ($term) {
$query->where('title', 'like', "%{$term}%")
->orWhere('body', 'like', "%{$term}%");
});
}
}
Define protected array $filterMethodMap when you need to alias request keys to method names. Programmatic filters can be appended with appendFilterable('key', $value) before apply() runs. Supplying an Illuminate\Contracts\Cache\Repository or Psr\Log\LoggerInterface to the constructor immediately enables the caching and logging features.
3. Attach the scope to a model
<?php
namespace App\Models;
use Filterable\Traits\Filterable;
use Illuminate\Database\Eloquent\Model;
class Post extends Model
{
use Filterable;
}
4. Run the filter pipeline
<?php
namespace App\Http\Controllers;
use App\Filters\PostFilter;
use App\Http\Resources\PostResource;
use App\Models\Post;
use Illuminate\Http\Request;
class PostController
{
public function index(Request $request, PostFilter $filter)
{
$posts = Post::query()
->filter(
$filter
->forUser($request->user())
->enableFeature('caching')
->setOptions(['chunk_size' => 500])
)
->get();
return PostResource::collection($posts);
}
}
apply() may only be called once per instance; call reset() if you need to reuse a filter. Because the Filter base class uses Laravel's Conditionable trait, you can use helpers such as $filter->when($request->boolean('validate'), fn ($filter) => $filter->enableFeature('validation'));.
Lifecycle & Core API
apply(Builder $builder, ?array $options = [])binds the filter to a query, merges options, runs enabled concerns, and transitions the state frominitialized→applying→applied. Re-applying withoutreset()raises aRuntimeException.get()returns anIlluminate\Support\Collectionof results, delegating to caching or memory-managed helpers when those features are active.runQuery()is a convenience wrapper forapply()+get().count()respects smart caching (including tagged caches and memoised counts when enabled).toSql()exposes the raw SQL for debugging.enableFeature(),enableFeatures(),disableFeature(),hasFeature()toggle concerns per instance; defaults may be set inconfig/filterable.phpand are applied in the constructor.setOption(),setOptions()persist runtime flags (for examplechunk_size,use_chunking) that concerns such asOptimizesQueriesandManagesMemoryconsume.reset()returns the filter to theinitializedstate so it can be applied again.getDebugInfo()surfaces state, filters applied, options, SQL/bindings, and metrics.
Feature Guides & API
Validation & Value Transformation
- Enable with
enableFeature('validation')and configure withsetValidationRules(),addValidationRule(), andsetValidationMessages().

