PHPackages                             kettasoft/filterable - PHPackages - PHPackages  [Skip to content](#main-content)[PHPackages](/)[Directory](/)[Categories](/categories)[Trending](/trending)[Leaderboard](/leaderboard)[Changelog](/changelog)[Analyze](/analyze)[Collections](/collections)[Log in](/login)[Sign up](/register)

1. [Directory](/)
2. /
3. [Database &amp; ORM](/categories/database)
4. /
5. kettasoft/filterable

ActiveLibrary[Database &amp; ORM](/categories/database)

kettasoft/filterable
====================

A powerful Laravel package for advanced Eloquent query filtering using dedicated filter classes, multiple engines, and clean HTTP request mapping.

v2.15.0(2mo ago)12641MITPHPPHP ^8.0CI passing

Since May 22Pushed 1mo ago1 watchersCompare

[ Source](https://github.com/kettasoft/filterable)[ Packagist](https://packagist.org/packages/kettasoft/filterable)[ RSS](/packages/kettasoft-filterable/feed)WikiDiscussions master Synced 1mo ago

READMEChangelog (10)Dependencies (12)Versions (35)Used By (0)

[![Filterable Logo](https://github.com/kettasoft/filterable/raw/docs/images/logo.png)](https://github.com/kettasoft/filterable/blob/docs/images/logo.png)

Filterable
==========

[](#filterable)

A powerful and flexible Laravel package for advanced, clean, and scalable filtering of Eloquent models using multiple customizable engines.

[![Tests](https://github.com/kettasoft/filterable/actions/workflows/php.yml/badge.svg?style=flat-square)](https://packagist.org/packages/kettasoft/filterable)[![Latest Version on Packagist](https://camo.githubusercontent.com/3dc7289437984aa6680d006faeb06a625d84003869d2e9b1b385b8ac434e0f4b/687474703a2f2f706f7365722e707567782e6f72672f6b65747461736f66742f66696c74657261626c652f763f7374796c653d666c61742d737175617265)](https://packagist.org/packages/kettasoft/filterable)[![Total Downloads](https://camo.githubusercontent.com/c4f27ce47d9a8c9ff5353123355c904a71e291ac5c3f5c373c9dd267a53bfc7c/68747470733a2f2f696d672e736869656c64732e696f2f7061636b61676973742f64742f6b65747461736f66742f66696c74657261626c653f7374796c653d666c61742d737175617265)](https://packagist.org/packages/kettasoft/filterable)[![License](https://camo.githubusercontent.com/3cd16f7e8a79bb28af036042c21a83cf933857190290321ee50ebe726377517b/68747470733a2f2f706f7365722e707567782e6f72672f6b65747461736f66742f66696c74657261626c652f6c6963656e73653f7374796c653d666c61742d737175617265)](https://github.com/kettasoft/filterable/blob/master/LICENSE)

Why Filterable?
---------------

[](#why-filterable)

Most filtering packages give you one approach and expect you to fit your problem around it. Filterable works the other way — you pick the **engine** that matches how your frontend sends data, and the package handles the rest.

It ships with four production-ready engines, a full caching system, per-filter authorization, validation, sanitization, sorting, a CLI, and an event system — all while keeping your controllers clean and your filter logic organized and testable.

---

Installation
------------

[](#installation)

```
composer require kettasoft/filterable
```

```
php artisan vendor:publish --provider="Kettasoft\Filterable\Providers\FilterableServiceProvider" --tag="config"
```

Add the following line to the providers array in config/app.php or bootstrap/providers.php:

```
'providers' => [
    ...

    Kettasoft\Filterable\Providers\FilterableServiceProvider::class,
];
```

---

Quick Start
-----------

[](#quick-start)

**1. Create a filter class**

```
php artisan filterable:make-filter PostFilter --filters=title,status
```

**2. Define your filters**

```
namespace App\Http\Filters;

use Kettasoft\Filterable\Filterable;
use Kettasoft\Filterable\Support\Payload;

class PostFilter extends Filterable
{
    protected $filters = ['status', 'title'];

    protected function title(Payload $payload)
    {
        return $this->builder->where('title', 'like', $payload->asLike('both'));
    }

    protected function status(Payload $payload)
    {
        return $this->builder->where('status', $payload->value);
    }
}
```

**3. Apply in your controller**

```
$posts = Post::filter(PostFilter::class)->paginate();
```

**4. Or bind the filter directly to the model**

```
class Post extends Model
{
    use HasFilterable;

    protected $filterable = PostFilter::class;
}

// Now just:
$posts = Post::filter()->paginate();
```

---

Choosing an Engine
------------------

[](#choosing-an-engine)

Each engine is designed for a different filtering style. Pick the one that fits your use case — or mix and match across different models.

EngineBest ForExample Request**Invokable**Custom logic per field, method-per-filter pattern`?status=active&title=laravel`**Ruleset**Clean key/operator/value API queries`?filter[title][like]=laravel&filter[views][gte]=100`**Expression**Ruleset-style + filtering through nested relations`?filter[author.profile.name][like]=ahmed`**Tree**Complex AND/OR nested logic sent as JSON`{ "and": [{ "field": "status", ... }] }`### Invokable Engine

[](#invokable-engine)

Map request keys to methods automatically. Add PHP 8 annotations for per-method sanitization, casting, validation, and authorization with zero boilerplate.

```
class PostFilter extends Filterable
{
    protected $filters = ['status', 'created_at'];

    #[Cast('integer')]
    #[DefaultValue(1)]
    protected function status(Payload $payload) { ... }

    #[SkipIf('auth()->guest()')]
    #[Between(min: '2020-01-01', max: 'now')]
    protected function created_at(Payload $payload) { ... }
}
```

Available annotations: `#[Authorize]` `#[SkipIf]` `#[Cast]` `#[Sanitize]` `#[Trim]` `#[DefaultValue]` `#[MapValue]` `#[Explode]` `#[Required]` `#[In]` `#[Between]` `#[Regex]` `#[Scope]`

### Ruleset Engine

[](#ruleset-engine)

Flat field-operator-value format, ideal for REST APIs where the frontend controls which operator to use.

```
GET /posts?filter[status]=published
GET /posts?filter[title][like]=%laravel%
GET /posts?filter[views][gte]=100
GET /posts?filter[id][in][]=1&filter[id][in][]=2

```

Supported operators: `eq` `neq` `gt` `gte` `lt` `lte` `like` `nlike` `in` `between`

### Expression Engine

[](#expression-engine)

Everything Ruleset does, plus filtering through deep Eloquent relationships using dot notation.

```
GET /posts?filter[author.profile.name][like]=ahmed

```

```
Filterable::create()
    ->useEngine('expression')
    ->allowedFields(['status', 'title'])
    ->allowRelations(['author.profile' => ['name']])
    ->paginate();
```

### Tree Engine

[](#tree-engine)

Send a nested AND/OR JSON tree — the engine recursively translates it into Eloquent `where` / `orWhere` groups.

```
{
    "filter": {
        "and": [
            { "field": "status", "operator": "eq", "value": "active" },
            {
                "or": [
                    { "field": "age", "operator": "gt", "value": 25 },
                    { "field": "city", "operator": "eq", "value": "Cairo" }
                ]
            }
        ]
    }
}
```

Supports depth limiting, strict operator whitelisting, and normalized field keys.

---

Features
--------

[](#features)

### Caching

[](#caching)

A complete caching system built into the filter pipeline — not bolted on after the fact.

```
// Cache for 1 hour
Post::filter()->cache(3600)->get();

// User-scoped cache (each user gets their own)
Post::filter()->cache(1800)->scopeByUser()->get();

// Tenant-isolated cache
Product::filter()->cache(3600)->scopeByTenant(tenant()->id)->get();

// Conditional cache
Model::filter()->cacheWhen(!auth()->user()->isAdmin(), 3600)->get();

// Tagged cache with easy invalidation
Post::filter()->cache(3600)->cacheTags(['posts', 'content'])->get();
Post::flushCacheByTagsStatic(['posts']);

// Reusable profiles defined in config
Report::filter()->cacheProfile('heavy_reports')->get();
```

Auto-invalidation: configure models and tags in `config/filterable.php` and caches are cleared automatically on create/update/delete.

### Authorization

[](#authorization)

Protect entire filter classes based on roles or permissions.

```
class AdminFilter extends Filterable
{
    public function authorize(): bool
    {
        return auth()->user()?->isSuperAdmin() ?? false;
    }
}
```

Per-method authorization is also available via the `#[Authorize]` annotation in the Invokable engine.

### Validation &amp; Sanitization

[](#validation--sanitization)

Validation rules and sanitizers are defined directly on the filter class — input is cleaned and validated before any filtering logic runs.

**Validation** uses Laravel's native rules format via a `$rules` property:

```
class PostFilter extends Filterable
{
    protected $rules = [
        'status' => ['required', 'string', 'in:active,pending,archived'],
        'title'  => ['sometimes', 'string', 'max:32'],
    ];
}
```

If validation fails, a `ValidationException` is thrown automatically — no extra handling needed in your controller.

**Sanitization** runs *before* validation, via dedicated sanitizer classes:

```
class PostFilter extends Filterable
{
    protected $sanitizers = [
        TrimSanitizer::class,        // global — applies to all fields
        'title' => [
            StripTagsSanitizer::class,
            CapitalizeSanitizer::class,
        ],
    ];
}
```

A sanitizer is a simple class implementing the `Sanitizable` interface:

```
class TrimSanitizer implements Sanitizable
{
    public function sanitize(mixed $value): mixed
    {
        return is_string($value) ? trim($value) : $value;
    }
}
```

The execution order is always: **sanitize → validate → filter**.

### Sorting

[](#sorting)

Built-in sorting support with allowed-field whitelisting.

```
class PostFilter extends Filterable
{
    protected $sortable = ['created_at', 'views', 'title'];
}

// GET /posts?sort=-created_at (descending)
// GET /posts?sort=views       (ascending)
```

### Event System

[](#event-system)

Hook into the filter lifecycle to add logging, metrics, or custom behavior.

```
// Fired before filters are applied
Event::listen(FilterApplying::class, fn($e) => Log::info('Filtering '.$e->model));

// Fired after filters are applied
Event::listen(FilterApplied::class, fn($e) => $metrics->record($e));
```

### Profile Management &amp; Profiler

[](#profile-management--profiler)

Save and reuse filter configurations, and inspect exactly what queries each filter generates.

### Lifecycle Hooks

[](#lifecycle-hooks)

`initially()` and `finally()` hooks let you modify the query builder before or after filtering runs.

---

CLI
---

[](#cli)

```
# Generate a new filter class with interactive setup
php artisan filterable:setup PostFilter

# Discover and auto-register all filter classes in your app
php artisan filterable:discover

# List all registered filters
php artisan filterable:list

# Test a filter class with a sample data string (key=value pairs)
php artisan filterable:test {filter} --model=User --data="status=active,age=30"

# Inspect a filter class (engines, fields, rules, etc.)
php artisan filterable:inspect PostFilter
```

---

Requirements
------------

[](#requirements)

- PHP 8.1+
- Laravel 10.x or higher
- Redis or Memcached recommended for tagged caching

---

📚 Documentation
---------------

[](#-documentation)

For full documentation, installation, and usage examples, visit: **[kettasoft.github.io/filterable](https://kettasoft.github.io/filterable)**

- [Introduction](https://kettasoft.github.io/filterable/introduction.html)
- [Engines Overview](https://kettasoft.github.io/filterable/engines/invokable/)
- [Caching System](https://kettasoft.github.io/filterable/caching/overview.html)
- [Authorization](https://kettasoft.github.io/filterable/authorization.html)
- [CLI Reference](https://kettasoft.github.io/filterable/cli/setup.html)
- [API Reference](https://kettasoft.github.io/filterable/api/filterable.html)

---

Contributing
------------

[](#contributing)

Found a bug or want to add an engine? PRs are welcome — please open an issue first to discuss.

License
-------

[](#license)

MIT © 2024-present [Kettasoft](https://github.com/kettasoft)

###  Health Score

44

—

FairBetter than 92% of packages

Maintenance87

Actively maintained with recent releases

Popularity17

Limited adoption so far

Community8

Small or concentrated contributor base

Maturity53

Maturing project, gaining track record

 Bus Factor1

Top contributor holds 100% of commits — single point of failure

How is this calculated?**Maintenance (25%)** — Last commit recency, latest release date, and issue-to-star ratio. Uses a 2-year decay window.

**Popularity (30%)** — Total and monthly downloads, GitHub stars, and forks. Logarithmic scaling prevents top-heavy scores.

**Community (15%)** — Contributors, dependents, forks, watchers, and maintainers. Measures real ecosystem engagement.

**Maturity (30%)** — Project age, version count, PHP version support, and release stability.

###  Release Activity

Cadence

Every ~10 days

Total

31

Last Release

50d ago

Major Versions

v1.0.0 → v2.0.02025-08-17

v2.15.0 → v3.x-dev2026-03-29

PHP version history (2 changes)v1.0.0PHP ^8.0

v3.x-devPHP ^8.2

### Community

Maintainers

![](https://www.gravatar.com/avatar/514a1d3f5416af98e8f0174f21cd94d357204a98704e92fd37db2903b08e2057?d=identicon)[kettasoft](/maintainers/kettasoft)

---

Top Contributors

[![kettasoft](https://avatars.githubusercontent.com/u/80687771?v=4)](https://github.com/kettasoft "kettasoft (242 commits)")

---

Tags

eloquentenginesfilterfilter-designfilterablefilteringfiltering-datafiltersfiltersystemlaravelmodelphpquery-buildersearchsearch-enginesearchablesearchingsearchlaravelmodeleloquentqueryfilter

###  Code Quality

TestsPHPUnit

### Embed Badge

![Health badge](/badges/kettasoft-filterable/health.svg)

```
[![Health](https://phpackages.com/badges/kettasoft-filterable/health.svg)](https://phpackages.com/packages/kettasoft-filterable)
```

###  Alternatives

[tucker-eric/eloquentfilter

An Eloquent way to filter Eloquent Models

1.8k4.8M26](/packages/tucker-eric-eloquentfilter)[mehdi-fathi/eloquent-filter

Eloquent Filter adds custom filters automatically to your Eloquent Models in Laravel.It's easy to use and fully dynamic, just with sending the Query Strings to it.

450191.6k1](/packages/mehdi-fathi-eloquent-filter)[jedrzej/searchable

Searchable trait for Laravel's Eloquent models - filter your models using request parameters

127259.1k5](/packages/jedrzej-searchable)[jedrzej/pimpable

Laravel 4/5/6 package that allows to dynamically filter, sort and eager load relations for your models using request parameters

105179.0k1](/packages/jedrzej-pimpable)[aldemeery/sieve

A simple, clean and elegant way to filter Eloquent models.

1396.3k](/packages/aldemeery-sieve)[lacodix/laravel-model-filter

A Laravel package to filter, search and sort models with ease while fetching from database.

17649.9k](/packages/lacodix-laravel-model-filter)

PHPackages © 2026

[Directory](/)[Categories](/categories)[Trending](/trending)[Changelog](/changelog)[Analyze](/analyze)
