Графіки у Filament малюються бібліотекою Chart.js. Віджет повертає дані в її форматі.
php artisan make:filament-widget OrdersChart --chart
use Filament\Widgets\ChartWidget;
class OrdersChart extends ChartWidget
{
protected ?string $heading = 'Замовлення';
protected ?string $pollingInterval = null; // історичним даним опитування не потрібне
public ?string $filter = 'month';
protected function getFilters(): ?array
{
return [
'week' => 'Тиждень',
'month' => 'Місяць',
'year' => 'Рік',
];
}
protected function getData(): array
{
$days = match ($this->filter) {
'week' => 7,
'year' => 365,
default => 30,
};
$rows = Order::query()
->where('created_at', '>=', now()->subDays($days))
->selectRaw('DATE(created_at) as day, COUNT(*) as total')
->groupBy('day')
->orderBy('day')
->pluck('total', 'day');
return [
'datasets' => [
['label' => 'Замовлення', 'data' => $rows->values()->all()],
],
'labels' => $rows->keys()->all(),
];
}
protected function getType(): string
{
return 'line';
}
}
Типи графіків: line, bar, pie, doughnut, radar, polarArea, bubble, scatter. Опції Chart.js - через getOptions().
Готове рішення для часових рядів - пакет flowframe/laravel-trend, який рекомендує документація: Trend::model(Order::class)->between(...)->perDay()->count() повертає значення й дні без даних (агрегат з GROUP BY дні без замовлень просто пропускає, і графік виходить нерівномірним).
Пастки:
$filter- публічна властивість Livewire, її значення приходить з браузера. Використовувати її лише черезmatchз відомими варіантами, а не підставляти в запит напряму;- опитування за замовчуванням - кожні 5 секунд. Агрегат за рік щоп'ять секунд на кожному відкритому дашборді - марне навантаження. Для історичних графіків опитування вимикають;
- важкі агрегати - кешувати з ключем, що містить фільтр, або рахувати заздалегідь у таблицю статистики;
DATE(created_at)групує за часовим поясом бази - якщо база в UTC, а користувачі в Києві, межі днів «зсунуться».