Filament: Stats Widgets Above Table - Auto-Update with Filters

Quite often in Filament we need to add widgets with stats above the table. In this lesson, let's see how to auto-update those stats in the widget when the filter is set in the table.

widget data based on filter


DB Structure

We will have an Order Eloquent Model with an OrderResource Filament Resource for this simple example.

database/migrations/xxx_create_orders_table.php:

public function up(): void
{
Schema::create('orders', function (Blueprint $table) {
$table->id();
$table->string('number');
$table->string('status');
$table->timestamps();
});
}

Filament Table with Filter

We need to show a table with a custom filter for Filament Resource.

app/Filament/OrderResource.php:

use Illuminate\Support\Carbon;
use Illuminate\Database\Eloquent\Builder;
 
class OrderResource extends Resource
{
// ...
 
public static function table(Table $table): Table
{
return $table
->columns([
Tables\Columns\TextColumn::make('number'),
Tables\Columns\TextColumn::make('status'),
])
->filters([
Tables\Filters\Filter::make('created_at')
->form([
Forms\Components\DatePicker::make('created_from')
->placeholder(fn ($state): string => 'Dec 18, ' . now()->subYear()->format('Y')),
Forms\Components\DatePicker::make('created_until')
->placeholder(fn ($state): string => now()->format('M d, Y')),
])
->query(function (Builder $query, array $data): Builder {
return $query
->when(
$data['created_from'] ?? null,
fn (Builder $query, $date): Builder => $query->whereDate('created_at', '>=', $date),
)
->when(
$data['created_until'] ?? null,
fn (Builder $query, $date): Builder => $query->whereDate('created_at', '<=', $date),
);
})
->indicateUsing(function (array $data): array {
$indicators = [];
if ($data['created_from'] ?? null) {
$indicators['created_from'] = 'Order from ' . Carbon::parse($data['created_from'])->toFormattedDateString();
}
if ($data['created_until'] ?? null) {
$indicators['created_until'] = 'Order until ' . Carbon::parse($data['created_until'])->toFormattedDateString();
}
 
return $indicators;
}),
])
->actions([
Tables\Actions\EditAction::make(),
])
->bulkActions([
Tables\Actions\BulkActionGroup::make([
Tables\Actions\DeleteBulkAction::make(),
]),
]);
}
 
// ...
}

table with filter


Stats Widget Above the Table

And finally, let's add a stats overview widget. We will show the total number of orders and the number of orders based on the status.

app/Filament/Resources/OrderResource/Widgets/OrderStats.php:

use App\Models\Order;
use Filament\Widgets\StatsOverviewWidget;
use Filament\Widgets\StatsOverviewWidget\Stat;
 
class OrderStats extends StatsOverviewWidget
{
protected static ?string $pollingInterval = null;
 
protected function getStats(): array
{
return [
Stat::make('Total orders', Order::count()),
Stat::make('Confirmed Orders', Order::where('status', 'confirmed')->count()),
Stat::make('Cancelled Orders', Order::where('status', 'cancelled')->count()),
];
}
}

table with widget


Applying Table Query to Widgets

To update widgets data according to the tables query, we can use a method getPageTableQuery(). But to use this method, first, we must prepare widgets and list records classes.

We must add an ExposesTableToWidgets trait to the ListOrders class.

app/Filament/Resources/OrderResource/Pages/ListOrders.php:

use Filament\Pages\Concerns\ExposesTableToWidgets;
 
class ListOrders extends ListRecords
{
use ExposesTableToWidgets;
 
// ...
}

For the widget class, we must add an InteractsWithPageTable trait and add the getTablePage() method to specify the list's class. In our case, it is ListOrders.

app/Filament/Resources/OrderResource/Widgets/OrderStats.php:

use Filament\Widgets\Concerns\InteractsWithPageTable;
use App\Filament\Resources\OrderResource\Pages\ListOrders;
 
class OrderStats extends StatsOverviewWidget
{
use InteractsWithPageTable;
 
protected function getTablePage(): string
{
return ListOrders::class;
}
 
// ...
}

Instead of starting an Eloquent query with a Model, we can change that to $this->getPageTableQuery().

app/Filament/Resources/OrderResource/Widgets/OrderStats.php:

class OrderStats extends StatsOverviewWidget
{
// ...
 
protected function getStats(): array
{
return [
Stat::make('Total orders', Order::count()),
Stat::make('Confirmed Orders', Order::where('status', 'confirmed')->count()),
Stat::make('Cancelled Orders', Order::where('status', 'cancelled')->count()),
Stat::make('Total orders', $this->getPageTableQuery()->count()),
Stat::make('Confirmed Orders', $this->getPageTableQuery()->where('status', 'confirmed')->count()),
Stat::make('Cancelled Orders', $this->getPageTableQuery()->where('status', 'cancelled')->count()),
];
}
}

And that's it. Now, the base query will be taken from the table, and we can add the necessary conditions.

You can find the code in the GitHub repository.


If you want more Filament examples, you can find more real-life projects on our FilamentExamples.com.

No comments or questions yet...

Like our articles?

Become a Premium Member for $129/year or $29/month
What else you will get:
  • 59 courses (1057 lessons, total 42 h 44 min)
  • 78 long-form tutorials (one new every week)
  • access to project repositories
  • access to private Discord

Recent Premium Tutorials