Weave Code
Code Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Filament Header Filters Laravel Package

leek/filament-header-filters

Add inline filters to Filament table column headers. Attach any BaseFilter (selects, date pickers, min/max ranges, custom schemas) as a richer alternative to individual searchable fields. Works with Filament v4/v5, PHP 8.2+.

View on GitHub
Deep Wiki
Context7

Getting Started

  1. Installation:

    composer require leek/filament-header-filters
    

    Add the HasHeaderFilters trait to your ListRecords or custom Livewire table component:

    use Leek\FilamentHeaderFilters\Concerns\HasHeaderFilters;
    
    class ListOrders extends ListRecords
    {
        use HasHeaderFilters;
    }
    
  2. CSS Integration: Add the package stylesheet to your Filament panel theme after Filament’s theme import:

    @import '../../../../vendor/filament/filament/resources/css/theme.css';
    @import '../../../../vendor/leek/filament-header-filters/resources/css/filament-header-filters.css';
    

    Rebuild assets:

    npm run build
    
  3. First Use Case: Attach a SelectFilter to a column header for inline filtering:

    TextColumn::make('status')
        ->badge()
        ->headerFilter(
            SelectFilter::make('status')
                ->options(OrderStatus::class)
                ->native(false)
        )
    

Implementation Patterns

1. Common Filter Types

  • Dropdown Filters (exact match):
    TextColumn::make('status')
        ->headerFilter(
            SelectFilter::make('status')
                ->options(OrderStatus::class)
                ->searchable()
        )
    
  • Range Filters (min/max):
    TextColumn::make('price')
        ->headerFilter(
            Filter::make('price_range')
                ->columns(2)
                ->schema([
                    TextInput::make('min')->numeric(),
                    TextInput::make('max')->numeric(),
                ])
                ->query(fn (Builder $query, array $data) => $query
                    ->when($data['min'], fn ($q, $v) => $q->where('price', '>=', $v))
                    ->when($data['max'], fn ($q, $v) => $q->where('price', '<=', $v))
                )
        )
    
  • Date Ranges:
    TextColumn::make('created_at')
        ->headerFilter(
            Filter::make('date_range')
                ->columns(2)
                ->schema([
                    DatePicker::make('from')->native(false),
                    DatePicker::make('until')->native(false),
                ])
                ->query(fn (Builder $query, array $data) => $query
                    ->when($data['from'], fn ($q, $v) => $q->whereDate('created_at', '>=', $v))
                    ->when($data['until'], fn ($q, $v) => $q->whereDate('created_at', '<=', $v))
                )
        )
    

2. Workflow Integration

  • Shared State: Header filters sync with panel filters ($tableFilters). Use ->deferFilters() on the table if you need to control when filters apply.
  • Hidden Columns: Filters on hidden columns are automatically skipped in queries.
  • Reset Behavior: Use the global reset button (if enabled) or ->reset() on individual filters.

3. Custom Filter Logic

Extend BaseFilter for complex logic:

class CustomStatusFilter extends Filter
{
    protected string $columnName = 'status';

    public function query(Builder $query, array $data): Builder
    {
        return $query->where('status', $data['status'] ?? null);
    }
}

Attach it to a column:

TextColumn::make('status')->headerFilter(CustomStatusFilter::make());

4. Dynamic Filter Options

Fetch options dynamically (e.g., from a relationship):

SelectFilter::make('user_id')
    ->options(fn () => User::query()->pluck('name', 'id'))
    ->headerFilter()

5. Conditional Filters

Show/hide filters based on context:

TextColumn::make('priority')
    ->headerFilter(
        SelectFilter::make('priority')
            ->options(['low', 'medium', 'high'])
            ->visible(fn () => auth()->user()->can('filter_priority'))
    )

Gotchas and Tips

Pitfalls

  1. Initialization Order:

    • If using custom HasTable pages, ensure HasHeaderFilters is loaded after InteractsWithTable. The trait now registers filters post-initialization (fixed in v2.0.4).
    • Workaround: Explicitly call $this->table->getHeaderFilters() in mount() if issues persist.
  2. Stale State in Single-Select Filters:

    • Multi-select filters previously left stale array values in single-select filters (e.g., ['pending'] instead of 'pending'). Fixed in v2.0.4, but test edge cases like rapid toggling between single/multi-select modes.
  3. CSS Conflicts:

    • Override styles if dropdowns/dates pop outside the table. Target:
      .filament-header-filters {
          z-index: 1000 !important;
      }
      
    • Tip: Use native(false) for better styling consistency.
  4. Hidden Columns:

    • Filters on hidden columns won’t apply to queries. Use ->visible() to toggle visibility dynamically.
  5. Filacheck False Positives:

    • The missing-table-filters rule may flag tables using only header filters. Disable it in config/filacheck.php:
      'missing-table-filters' => ['enabled' => false],
      
  6. Asset Rebuilding:

    • Forgetting to run npm run build after adding the CSS will break filter rendering. Use npm run dev in development.

Debugging Tips

  • Check Filter Registration:
    dd($this->table->getHeaderFilters());
    
  • Inspect Query: Use tap() to debug the query builder:
    ->query(fn (Builder $query) => $query->tap(fn ($q) => dd($q->toSql())))
    
  • Livewire State: Dump the form state:
    dd($this->getTableHeaderFiltersForm()->getState());
    

Extension Points

  1. Custom Filter Components: Extend BaseFilter and override getField() to use custom form components:

    class CustomFilter extends Filter
    {
        public function getField(): array
        {
            return [
                Toggle::make('active')->label('Active'),
            ];
        }
    }
    
  2. Override Table View: If Filament updates break the view override, copy filament-tables::index from the package to resources/views/vendor/filament-tables/ and modify as needed.

  3. Dynamic Column Names: Use ->columnName() in custom filters to dynamically set the column:

    Filter::make('dynamic_filter')
        ->columnName('dynamic_column')
        ->schema([/* ... */])
    
  4. Session Persistence: Leverage Filament’s built-in session persistence for filters. Ensure your table uses:

    ->persistFilters()
    

Performance Considerations

  • Avoid Over-Querying: Complex ->query() closures in filters can impact performance. Cache dynamic options (e.g., ->options(fn () => cache()->remember(...))).
  • Debounce Inputs: For text inputs, add debouncing to reduce query load:
    TextInput::make('search')
        ->debounce(500)
    
  • Lazy-Load Options: For large dropdowns, use ->searchable() and paginate options:
    SelectFilter::make('user_id')
        ->options(fn () => User::query()->paginate(20))
        ->searchable()
    

Testing

  • Unit Test Filters:
    public function test_header_filter()
    {
        $filter = SelectFilter::make('status')->options(['active', 'inactive']);
        $column = TextColumn::make('status')->headerFilter($filter);
        $this->assertTrue($column->hasHeaderFilter());
    }
    
  • Livewire Test State:
    $this->livewire(ListOrders::class)
        ->call('getTableHeaderFiltersForm')
        ->assertSet('status', 'active');
    
Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
besmartand-pro/php-quality-config
sentix/ai-chatbot
terminal42/code-quality-tools
codifyo/ts-generator-bundle
testo/fiber
mintobit/jobqueue
a4sex/maintenance-bundle
a4sex/entity-date-update
a4sex/client-identifier
a4sex/base-utilites
a4sex/key-value-storage
a4sex/micro-status
chilldev/dependency-injection-extra
datinglibre/datinglibre-app-api
biberltd/corebundle
bricre/symfony-bundle-test
biberltd/logbundle
dominium/http-adapter-bundle
dominium/google-analytics
a4sex/auto-clean-entity