santwer/exporter
Santwer Exporter is a Laravel/PHP package for exporting application data to downloadable files. It offers a simple API for defining exports and generating them from models or query results, streamlining data export workflows.
v0.5.0.4 is a patch on the 0.5.0.x line — no new API surface, so anything that worked on 0.5.0.3 should keep working. Treat this as a routine composer update but pin ^0.5.0 (not ^0.5) and read the compare diff if you're mid-debug, since a patch release after several quick iterations often carries small behaviour fixes rather than features.
composer require santwer/exporter:^0.5.0
php artisan vendor:publish --tag=exporter-config
config/exporter.php — the config is the source of truth for default disk, file format, and column mapping. Confirm filesystems.disks has the disk you intend to write to (usually local or s3), because that mismatch is the #1 first-run failure.Thin export definitions, fat queries. Keep the mapping from model to output columns in a dedicated export class and keep the query building outside it (repository, scope, or a builder the caller passes in). Day-to-day you'll want the same export reused for "current filter" and "everything" — if the export class owns the where clauses you'll end up duplicating it.
Always drive large exports through the queue. A few hundred rows inline is fine; anything user-triggered against a table you don't control should be a queued job that writes to disk and then notifies (mail/database notification) with a signed temporary URL. Pattern:
202-ish UI state.Chunk your reads. chunkById() (not chunk(), which drifts if rows are inserted mid-run) sized to a few thousand rows, with eager-loaded relations to avoid N+1 — an export is the single easiest place to accidentally fire a query per row.
Stream instead of accumulating. If the package lets you write row-by-row to the output handle, do that; concatenating every row into an array is what turns a 200k-row export into a memory-limit fatal.
Integrate at the boundaries you already have. One artisan command (exports:prune, exports:run-scheduled) plus one scheduled entry is usually enough; avoid a bespoke admin controller per export type — a single ExportController with an exportType enum/registry keeps route and authorization logic in one place.
Test with Storage::fake(). Assert the file exists, assert the header row and a couple of known data rows by parsing the output back, and assert row count matches the filtered query count. This catches column-order and date-format regressions that no amount of manual clicking will.
0.5.x means minor bumps may break you. Pin to ~0.5.0 in composer.json, and diff the changelog before every minor. Don't let a composer update in CI silently move you.v0.5.0.4 ships nothing but a compare link. Read the actual diff (git diff v0.5.0.3..v0.5.0.4) before assuming it's inert — quiet patches in young packages sometimes change a default or fix something your workaround depends on.max_execution_time, memory limit, and PHP-FPM's request_terminate_timeout all bite before any Laravel-level timeout. Set the queue worker's --timeout below retry_after or you'll get duplicated exports.Carbon values serialize with the app timezone; if the spreadsheet consumer is elsewhere, convert explicitly at mapping time instead of debugging "off by one day" later.config/exporter.php in production, php artisan config:cache — otherwise the old defaults persist and you'll swear the package is ignoring you.Storage::url() on the local disk often fails in production because the public symlink doesn't exist. Prefer a controller route with Storage::download() over exposing the path.exports:prune to the scheduler on day one; the first disk-full incident is otherwise a certainty.How can I help you explore Laravel packages today?