agriserv / exports
Queued spreadsheet exports for Laravel apps. Wraps maatwebsite/excel with a tracked ExportJob + Livewire status UI.
Requires
- php: ^8.2
- illuminate/bus: ^10.0|^11.0|^12.0
- illuminate/console: ^10.0|^11.0|^12.0
- illuminate/database: ^10.0|^11.0|^12.0
- illuminate/http: ^10.0|^11.0|^12.0
- illuminate/queue: ^10.0|^11.0|^12.0
- illuminate/support: ^10.0|^11.0|^12.0
- livewire/livewire: ^3.0
- maatwebsite/excel: ^3.1
README
Queued spreadsheet exports for Laravel. Wraps maatwebsite/excel with an
ExportJob tracking row and a Livewire status UI so requests don't time out
on heavy reports.
Install
composer require agriserv/exports php artisan vendor:publish --tag=exports-config # optional php artisan vendor:publish --tag=exports-migrations # optional php artisan migrate
The service provider and QueuedExports facade are auto-discovered.
Requires PHP ^8.2, Laravel 10/11/12, maatwebsite/excel ^3.1, and
livewire/livewire ^3.0.
Configure (env)
EXPORTS_DISK=local # any filesystem disk EXPORTS_PATH=exports # subfolder on that disk EXPORTS_QUEUE=exports # queue name workers listen on EXPORTS_QUEUE_CONNECTION= # leave blank to use default EXPORTS_TRIES=3 EXPORTS_TIMEOUT=1800 EXPORTS_RETENTION_DAYS=7 EXPORTS_ROUTE_PREFIX=exports
Make sure a queue worker listens on the configured queue:
php artisan queue:work --queue=exports
Usage
Replace synchronous Excel::download(...) calls with:
use Agriserv\Exports\Facades\QueuedExports; use App\Exports\InvoicesExport; public function export() { $query = Invoice::query()->filter(request()->all()); // your existing Builder $job = QueuedExports::queue( new InvoicesExport($query), filename: 'invoices-' . now()->format('Y-m-d-His') . '.xlsx', options: [ 'label' => 'تصدير الفواتير', // 'user' => auth()->user(), // defaults to auth()->user() // 'disk' => 's3', // override default disk // 'format' => 'xlsx', // xlsx (default) | csv | tsv | ods | html ], ); return back()->with('status', "تم إضافة طلب التصدير إلى الطابور (#{$job->id})."); }
Picking a format
| Format | When to use |
|---|---|
xlsx (default) |
Most cases. Supports styling, RTL events, multi-sheet. Whole workbook held in RAM until save — heavy on large datasets. |
csv |
Large datasets (50k+ rows). Streams cell-by-cell, memory stays flat regardless of row count. No styling. For Arabic, the export class should use WithCustomCsvSettings returning ['use_bom' => true] so Excel renders UTF-8 correctly. |
tsv / ods / html |
Niche; same streaming/styling trade-offs as csv. |
The user sees their export appear in the status component:
<livewire:exports.list />
The component polls every 4s while any export is pending/processing, then shows a download button (signed URL, 30 min) when complete.
Pruning
php artisan exports:prune # uses config retention_days
php artisan exports:prune --days=30
php artisan exports:prune --dry-run
Schedule it in routes/console.php:
Schedule::command('exports:prune')->dailyAt('03:00');
Notes on existing exports
Your existing Maatwebsite export classes work as-is — pass them through
QueuedExports::queue() instead of Excel::download(). The only caveat
for queueing: the export instance is serialized onto the queue, so its
constructor properties (Eloquent Builders, Collections) must be
serializable. Don't put closures inside the query — rebuild the
query from saved filters inside the export class instead.
For large datasets, add implements WithChunkReading and a chunkSize()
method to your FromQuery export so it reads in batches rather than
loading everything into memory.