K

Table

Patterns & Recipes

On this page

E-commerce Product Filters

$table->filters([
SelectFilter::make('category')
->options(Category::orderBy('name')->pluck('name', 'id')->toArray())
->searchable()
->query(fn (Builder $q, string $v) => $q->whereHas('category', fn ($q) => $q->where('id', $v))),
 
SelectFilter::make('brand')
->options(Brand::orderBy('name')->pluck('name', 'id')->toArray())
->searchable()
->multiple(),
 
NumberRangeFilter::make('price')
->min(0)
->max(100000)
->step(100)
->minLabel('Min Price')
->maxLabel('Max Price'),
 
TernaryFilter::make('in_stock')
->label('Availability')
->trueLabel('In Stock')
->falseLabel('Out of Stock')
->query(fn (Builder $q, bool $value) => $value
? $q->where('stock_quantity', '>', 0)
: $q->where('stock_quantity', '<=', 0)),
 
SelectFilter::make('rating')
->options([
'5' => '★★★★★',
'4' => '★★★★☆ and up',
'3' => '★★★☆☆ and up',
])
->query(fn (Builder $q, string $v) => $q->where('avg_rating', '>=', (int)$v)),
 
TernaryFilter::make('has_discount')
->label('Discounted')
->query(fn (Builder $q, bool $value) => $value
? $q->whereNotNull('discount_percent')
: $q->whereNull('discount_percent')),
]);

Admin User Filters

$table->filters([
SelectFilter::make('role')
->options(Role::pluck('display_name', 'name')->toArray())
->multiple(),
 
TernaryFilter::make('email_verified_at')
->label('Email Verified')
->nullable(),
 
DateFilter::make('created_at')
->range()
->fromLabel('Registered after')
->toLabel('Registered before'),
 
TernaryFilter::make('two_factor_enabled')
->label('2FA Enabled'),
 
SelectFilter::make('last_activity')
->label('Activity')
->options([
'today' => 'Active today',
'week' => 'Active this week',
'month' => 'Active this month',
'inactive' => 'Inactive (30+ days)',
])
->query(fn (Builder $q, string $v) => match($v) {
'today' => $q->whereDate('last_active_at', today()),
'week' => $q->where('last_active_at', '>=', now()->subWeek()),
'month' => $q->where('last_active_at', '>=', now()->subMonth()),
'inactive' => $q->where('last_active_at', '<', now()->subMonth()),
}),
]);

Order Management Filters

$table->filters([
SelectFilter::make('status')
->options([
'pending' => 'Pending',
'processing' => 'Processing',
'shipped' => 'Shipped',
'delivered' => 'Delivered',
'cancelled' => 'Cancelled',
'refunded' => 'Refunded',
])
->multiple()
->default(['pending', 'processing']), // pre-select active orders
 
DateFilter::make('ordered_at')
->range(),
 
NumberRangeFilter::make('total')
->min(0)
->max(1000000)
->step(100)
->minLabel('Min Total')
->maxLabel('Max Total'),
 
SelectFilter::make('payment_method')
->options([
'card' => 'Credit Card',
'bank' => 'Bank Transfer',
'cash' => 'Cash on Delivery',
]),
 
TernaryFilter::make('has_notes')
->label('Has Notes')
->query(fn (Builder $q, bool $value) => $value
? $q->whereNotNull('notes')->where('notes', '!=', '')
: $q->where(fn ($q2) => $q2->whereNull('notes')->orWhere('notes', ''))),
]);

Filter with Dependent Options

// Country → City cascade (requires Livewire re-render)
SelectFilter::make('country')
->options(Country::pluck('name', 'id')->toArray())
->searchable(),
 
SelectFilter::make('city')
->options(function () {
$countryId = $this->tableFilters['country'] ?? null;
if (! $countryId) {
return [];
}
return City::where('country_id', $countryId)->pluck('name', 'id')->toArray();
})
->searchable()
->visible(fn () => ! empty($this->tableFilters['country'])),