Найчастіша причина повільної таблиці в адмінці - запит на кожен рядок для підрахунку пов'язаних записів. Filament має для цього методи, які додають агрегат в основний запит (через withCount, withSum тощо).
use Filament\Tables\Columns\TextColumn;
use Illuminate\Database\Eloquent\Builder;
// кількість
TextColumn::make('orders_count')
->counts('orders')
->sortable(),
// кількість зі скоупом
TextColumn::make('orders_count')
->counts(['orders' => fn (Builder $query) => $query->where('status', 'paid')]),
// чи є хоча б один
TextColumn::make('orders_exists')
->exists('orders'),
// сума, середнє, мінімум, максимум
TextColumn::make('orders_sum_total')
->sum('orders', 'total')
->money('UAH'),
TextColumn::make('reviews_avg_rating')
->avg('reviews', 'rating')
->numeric(decimalPlaces: 1),
Ім'я колонки обов'язково за конвенцією Laravel: {зв'язок}_count, {зв'язок}_exists, {зв'язок}_{функція}_{поле}. Саме під таким ім'ям Eloquent кладе результат у модель.
Чому не state():
// N+1: окремий SELECT COUNT на кожен рядок сторінки
TextColumn::make('orders')
->state(fn (Customer $record): int => $record->orders()->count()),
На сторінці з 50 рядками - 50 додаткових запитів. Агрегатні методи дають один запит з підзапитами, і по таких колонках ще й можна сортувати.
Інші джерела N+1 у таблицях:
- крапкова нотація (
author.name) Filament завантажує жадібно сам; - але зв'язки, до яких звертаються всередині колбеків
description(),color(),url(), - ні. Їх треба підвантажити явно:
->modifyQueryUsing(fn (Builder $query) => $query->with(['author.media', 'category']))
Як ловити: Model::preventLazyLoading() у локальному оточенні - лінивий доступ до незавантаженого зв'язку кидає виняток, і проблема видна одразу, а не на продакшені.