ExportAction формує CSV чи XLSX у фоні: користувач вибирає колонки, отримує сповіщення, коли файл готовий, і завантажує його. Під капотом - пакети джоб і сповіщення в базі даних.
php artisan make:filament-exporter Order --generate
use Filament\Actions\Exports\ExportColumn;
use Filament\Actions\Exports\Exporter;
class OrderExporter extends Exporter
{
protected static ?string $model = Order::class;
public static function getColumns(): array
{
return [
ExportColumn::make('number'),
ExportColumn::make('customer.name')->label('Клієнт'),
ExportColumn::make('items_count')->counts('items'),
ExportColumn::make('total'),
ExportColumn::make('created_at'),
];
}
}
use Filament\Actions\ExportAction;
use Filament\Actions\Exports\Enums\ExportFormat;
use Illuminate\Database\Eloquent\Builder;
ExportAction::make()
->exporter(OrderExporter::class)
->formats([ExportFormat::Xlsx, ExportFormat::Csv])
->modifyQueryUsing(fn (Builder $query) => $query->whereBelongsTo(auth()->user()->team))
->fileDisk('s3')
->chunkSize(500)
->maxRows(100_000);
Звідки беруться дані. У таблиці експорт бере поточний запит таблиці - з пошуком, фільтрами й сортуванням. Користувач експортує те, що бачить у списку (усі сторінки).
Безпека:
- політики для кожного запису не перевіряються. Якщо таблиця показує лише «свої» записи завдяки скоупу ресурсу - добре. Але експорт поза таблицею (з кнопки на сторінці) бере всю модель. Обмеження варто дублювати в
modifyQueryUsing()або вmodifyQuery()експортера; - диск. Готові файли зберігаються на диску. Filament навмисно уникає публічного диска (якщо диск за замовчуванням
publicі єlocal, експорт піде вlocal), але на продакшені краще явно вказати приватнийs3/r2; - CSV-формули. Значення з бази потрапляють у файл як є. Текст, що починається з
=,+,-,@і вводився користувачами, Excel може виконати. Захист -formatStateUsing()на колонках з довільним текстом (наприклад, префікс'); - чутливі колонки (телефони, email, суми) варто не додавати в експортер «про всяк випадок» - користувач вибирає з того, що ви дозволили.
Продуктивність:
- експорт ділиться на частини (за замовчуванням по 100 рядків) - для простих рядків
chunkSize()можна збільшити, для важких зменшити; - зв'язки й агрегати в колонках варто жадібно завантажити в
modifyQuery()експортера, інакше кожна частина робить N+1; maxRows()не дасть випадково поставити в чергу мільйонний експорт.