ptplugins/filament-auto-filters

Automatic table filters for FilamentPHP based on column definitions

Maintainers

Package info

github.com/ptplugins/filament-auto-filters

pkg:composer/ptplugins/filament-auto-filters

Statistics

Installs: 0

Dependents: 0

Suggesters: 0

Stars: 0

Open Issues: 0

1.0.0 2026-05-04 21:26 UTC

This package is not auto-updated.

Last update: 2026-05-05 13:06:50 UTC


README

Filament Auto Filters — auto-generated filter panel for a Filament resource

Automatic table filters for FilamentPHP v3, v4, and v5 based on your column definitions. Stop writing repetitive filter code — just define your columns and get smart filters for free.

Single codebase across all three Filament major versions — same trait, same API.

The Problem

Every Filament resource needs filters. For a typical table with 10 columns, you end up writing 10 filter definitions — most of which follow the same patterns. Date columns get date pickers, text columns get search inputs, relationships get whereHas queries.

Before (manual filters for each column):

public static function table(Table $table): Table
{
    return $table
        ->columns([
            TextColumn::make('name'),
            TextColumn::make('email'),
            TextColumn::make('department.name'),
            TextColumn::make('hired_at')->date(),
            TextColumn::make('data.position'),
        ])
        ->filters([
            Filter::make('name')
                ->form([TextInput::make('value')->label('Name')])
                ->query(fn (Builder $q, array $data) => /* ... */),
            Filter::make('email')
                ->form([TextInput::make('value')->label('Email')])
                ->query(fn (Builder $q, array $data) => /* ... */),
            Filter::make('department.name')
                ->form([TextInput::make('value')->label('Department')])
                ->query(fn (Builder $q, array $data) => $q->whereRelation(/* ... */)),
            Filter::make('hired_at')
                ->form([DatePicker::make('from'), DatePicker::make('until')])
                ->query(fn (Builder $q, array $data) => /* ... */),
            Filter::make('data.position')
                ->form([TextInput::make('value')->label('Position')])
                ->query(fn (Builder $q, array $data) => /* ... using JSON arrow notation */),
        ]);
}

After (one line):

->filters(static::autoFilters($table))

Installation

composer require ptplugins/filament-auto-filters

The package auto-discovers its service provider. No manual registration needed.

Publish Configuration (optional)

php artisan vendor:publish --tag=auto-filters-config

Quick Start

Add the trait to your Filament resource and call autoFilters():

use PtPlugins\FilamentAutoFilters\Concerns\HasAutoFilters;

class EmployeeResource extends Resource
{
    use HasAutoFilters;

    public static function table(Table $table): Table
    {
        return $table
            ->columns([/* ... */])
            ->filters(static::autoFilters($table));
    }
}

That's it. Every TextColumn in your table now has a filter.

Full Example: Employee Management

Let's walk through a real-world scenario. You're building an HR module with an Employee model that has direct columns, a department relationship, and a JSON data column for flexible fields.

The Model

class Employee extends Model
{
    // Direct columns
    const NAME = 'name';
    const EMAIL = 'email';
    const HIRED_AT = 'hired_at';
    const SALARY = 'salary';

    // JSON data column fields
    const D_POSITION = 'data.position';
    const D_OFFICE = 'data.office';

    // Relationships
    const R_DEPARTMENT_NAME = 'department.name';
    const R_MANAGER_NAME = 'manager.name';

    protected $casts = [
        'hired_at' => 'date',
        'data' => 'array',
    ];

    public function department(): BelongsTo
    {
        return $this->belongsTo(Department::class);
    }

    public function manager(): BelongsTo
    {
        return $this->belongsTo(Employee::class, 'manager_id');
    }
}

The Resource

use Filament\Tables\Columns\TextColumn;
use Filament\Tables\Table;
use PtPlugins\FilamentAutoFilters\Concerns\HasAutoFilters;

class EmployeeResource extends Resource
{
    use HasAutoFilters;

    protected static ?string $model = Employee::class;

    public static function table(Table $table): Table
    {
        return $table
            ->columns([
                TextColumn::make(Employee::NAME)
                    ->label('Name')
                    ->searchable(),

                TextColumn::make(Employee::EMAIL)
                    ->label('Email'),

                TextColumn::make(Employee::R_DEPARTMENT_NAME)
                    ->label('Department'),

                TextColumn::make(Employee::R_MANAGER_NAME)
                    ->label('Manager'),

                TextColumn::make(Employee::HIRED_AT)
                    ->label('Hired')
                    ->date(),

                TextColumn::make(Employee::SALARY)
                    ->label('Salary')
                    ->numeric(),

                TextColumn::make(Employee::D_POSITION)
                    ->label('Position'),

                TextColumn::make(Employee::D_OFFICE)
                    ->label('Office'),
            ])
            ->filters(static::autoFilters($table));
    }
}

What Gets Generated

The plugin inspects each column and generates the right filter type. Detection rules:

Column Filter
TextColumn (date / datetime) Date range picker (from / until)
TextColumn (default) Text search (LIKE %...%)
TextColumn with dot notation rel.col Text search via whereRelation()
TextColumn with data.X prefix Text search via JSON arrow data->X
IconColumn->boolean() Ternary (Yes / No / All)
SelectColumn->options([...]) Select filter with same options
Other column types Skipped — pass an explicit filter via overrides

For our Employee example (8 columns), all 8 filters are generated from zero lines of filter code. Same applies to a table mixing IconColumn and SelectColumn — they get their right filter automatically.

Overriding Specific Filters

Auto-generated filters are great for most columns. But sometimes you need a SelectFilter with specific options, or custom logic. Pass your explicit filters as overrides — they replace any auto-generated filter with the same name:

->filters(static::autoFilters($table, overrides: [
    // Replace the auto-generated text filter for department
    // with a select dropdown instead
    static::makeSelectFilter(
        Employee::R_DEPARTMENT_NAME,
        'Department',
        Department::pluck('name', 'name')
    ),

    // Custom filter with your own logic
    SelectFilter::make(Employee::SALARY)
        ->label('Salary Range')
        ->options([
            'junior' => 'Under 50k',
            'mid' => '50k - 100k',
            'senior' => 'Over 100k',
        ]),
]))

Result: department.name and salary use your custom filters. The remaining 6 columns still get auto-generated filters.

Skipping Columns

Some columns don't need filters. Pass their names in the skip array:

->filters(static::autoFilters($table, skip: [
    'deleted_at',
    Employee::SALARY,
]))

Combining Overrides and Skips

->filters(static::autoFilters($table,
    overrides: [
        static::makeSelectFilter(
            Employee::R_DEPARTMENT_NAME,
            'Department',
            Department::pluck('name', 'name')
        ),
    ],
    skip: [
        'deleted_at',
    ]
))

Priority order:

  1. Override filters are always included first
  2. Columns matching an override name are skipped (no duplicates)
  3. Columns in the skip list are skipped
  4. Everything else gets an auto-generated filter

API Reference

autoFilters(Table $table, array $overrides = [], array $skip = []): array

The main method. Inspects every column in the table and generates an appropriate filter for TextColumn, IconColumn->boolean(), and SelectColumn. Other column types are skipped — pass them explicitly via overrides.

makeTernaryFilter(string $name, string $label): TernaryFilter

Creates a yes/no/all ternary filter for a boolean column. Handles direct, JSON, and relationship columns the same way as the other helpers.

makeSelectFilter(string $name, string $label, array|Closure $options): SelectFilter

Creates a select dropdown filter that automatically handles:

  • Direct columns — standard whereIn query
  • JSON columns (data.xxx) — uses attribute() with arrow notation
  • Relationship columns (rel.col) — uses whereHas query

By default, select filters are multiple-choice and searchable (configurable).

makeDateRangeFilter(string $name, string $label): Filter

Creates a date range filter with "from" and "until" date pickers. Handles relationship and JSON columns the same way.

makeTextFilter(string $name, string $label): Filter

Creates a text search filter (LIKE contains). Handles relationship and JSON columns automatically.

resolveColumn(string $name): array

Resolves a column name into its type and query components. Used internally, but available if you need to build custom filters with the same column-detection logic.

Returns:

  • ['type' => FilterType::Direct, 'query_column' => 'name']
  • ['type' => FilterType::Relationship, 'relationship' => 'department', 'column' => 'name']
  • ['type' => FilterType::Json, 'query_column' => 'data->position']

Configuration

Publish the config file to customize defaults:

php artisan vendor:publish --tag=auto-filters-config
// config/auto-filters.php
return [
    'date_filter_columns'     => 3,          // Form column layout for date range
    'text_search_placeholder' => 'Search...', // Placeholder for text inputs
    'date_format'             => 'd.m.Y',     // Display format in filter indicators
    'select_multiple'         => true,         // Allow multi-select by default
    'select_searchable'       => true,         // Searchable dropdowns by default
];

How Column Detection Works

The plugin uses a simple naming convention to detect column types:

name            → Direct column    → WHERE name LIKE '%...%'
hired_at        → Date column      → WHERE hired_at >= ? AND hired_at <= ?
department.name → Relationship     → whereHas('department', fn($q) => $q->where('name', ...))
data.position   → JSON column      → WHERE data->position LIKE '%...%'
  • Dot notation with a data. prefix → JSON arrow notation
  • Dot notation without data. prefix → Eloquent relationship
  • No dots → direct database column
  • Date/DateTime detection uses Filament's built-in isDate() / isDateTime() methods on TextColumn

Requirements

  • PHP 8.2+
  • FilamentPHP 3.x, 4.x, or 5.x (single codebase across all three)
  • Laravel 10, 11, or 12

License

MIT