Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Laravel Resource Exporter Laravel Package

sinemacula/laravel-resource-exporter

A Laravel package for exporting resource collections and application data to downloadable files. It provides a clean, configurable API for choosing fields and formats, reducing repetitive boilerplate and making reports, admin exports, and data handoff workflows easier to build.

View on GitHub
Deep Wiki
Context7

Getting Started

This package turns Eloquent models/API Resources into downloadable or stored export files (CSV/XLSX/etc.), so the first job is to confirm the version you're pulling and wire the config into your app.

composer require sinemacula/laravel-resource-exporter:^3.1
php artisan vendor:publish --tag=resource-exporter-config

Pin ^3.1 rather than ^3.0 — Laravel 13 support landed in 3.1.0, and the minor bump is the only thing standing between you and a framework-version conflict on a fresh Laravel 13 install. There are no breaking changes in 3.1.0, so existing 3.0.x exports keep working unchanged.

Where to look first

  1. The published config (config/resource-exporter.php): default disk, default format, chunk size, queue connection, temp-file handling. Most "why is my file empty / in the wrong place" issues are resolved here.
  2. The exporter base class / contract in the package source — this tells you exactly which methods you must implement (query, resource mapping, filename) versus which are optional hooks.
  3. The README's single end-to-end example; copy it verbatim before customising.

First use case: export a filtered index listing to CSV. Keep the mapping in your existing UserResource so the export and the JSON API never drift apart:

// Typical shape — confirm the base class/namespace against the README.
class UserExporter extends ResourceExporter
{
    protected function query(): Builder
    {
        return User::query()->with('team')->latest();
    }

    protected function resource(): string
    {
        return UserResource::class; // same resource your API returns
    }

    protected function filename(): string
    {
        return 'users-' . now()->format('Y-m-d-His') . '.csv';
    }
}

Then expose it either as a streamed download (small sets) or a queued job writing to Storage (anything non-trivial). Start with the synchronous download on a few hundred rows to validate the mapping, then move to the queue once you trust the output.

Implementation Patterns

Reuse API Resources as the column contract. The biggest long-term win is defining columns once, in Http/Resources, and letting the exporter render them. Header rows come from the resource keys, which means adding a field to the API automatically adds it to the export — and it means a failing export test is really an API-shape test.

Two delivery modes, chosen by row count.

// Small: stream straight to the browser.
return $exporter->download();

// Large: queue it, store it, hand back a signed URL.
ExportUsersJob::dispatch($filters, $request->user()->id);

Filters belong in a query object, not the exporter. Push request filtering into a dedicated builder method or query class and pass filters (arrays/scalars) into the job — never an Eloquent collection or a Builder instance. Serialising a builder into a queued job is the most common source of "works locally, fails on the worker" bugs.

final class ExportUsersJob implements ShouldQueue
{
    use Queueable;

    public function __construct(public array $filters, public int $userId) {}

    public function handle(UserExporter $exporter): void
    {
        $path = $exporter->forFilters($this->filters)->store();

        User::find($this->userId)?->notify(new ExportReady($path));
    }
}

Chunk, don't collect. The package's chunk size config is the knob, but you still need to make sure the underlying query streams. Prefer chunkById()/lazyById() semantics over get(); a 200k-row export that passes CI with 500 seed rows will OOM in production.

Format strategy. CSV for machine handoff and re-import, XLSX for finance/ops users who will touch the numbers, PDF only for fixed-layout reporting. Don't build all three; pick the one the consumer actually opens.

Testing. Fake the disk and assert structure rather than bytes:

Storage::fake('exports');
Queue::fake();

$this->post('/exports/users')->assertOk();
Queue::assertPushed(ExportUsersJob::class);

// In the job test:
Storage::disk('exports')->assertExists($path);
$this->assertSame($expectedHeaders, $this->csvHeaders($path));
$this->assertCount(3, $this->csvRows($path));

Assert header names and order explicitly — that's what catches a resource change silently reordering a finance team's spreadsheet.

Authorization before querying. Exports frequently bypass the per-record scoping that your controllers apply, because the exporter builds its own query. Apply policies/whereBelongsTo($user->team) inside the exporter's query, not just on the route.

Gotchas and Tips

  • Version constraints. Laravel 13 is supported from 3.1.0 only. If you're on Laravel 11/12 you can still take 3.1.x (no breaking changes), but check composer why-not before blaming the package for a resolution failure.
  • Config caching. Publish the config before config:cache, and re-run php artisan config:clear after changing the default disk. Stale config is the #1 cause of files appearing in unexpected locations.
  • CSV + Excel encoding. Add a UTF-8 BOM if the file is opened in Excel, or accented characters arrive mangled. Also guard against CSV formula injection: prefix any cell starting with =, +, -, or @.
  • Locale and delimiters. European Excel expects ; and comma decimals. If the package exposes a delimiter/locale option, set it per-format rather than post-processing the file.
  • N+1 in exports. Eager-load every relationship your resource touches. A resource that quietly lazy-loads one relation turns a 10k-row export into 10k queries inside a worker with no request timeout to save you.
  • Web timeouts. PHP-FPM and proxy timeouts will cut a synchronous export off mid-stream, producing a truncated but "successful-looking" file. Anything above a few thousand rows belongs on the queue, with $timeout and $tries set explicitly on the job.
  • Failed jobs and partial files. Write to a temp path and move/rename on success, so a failed job doesn't leave a half-written file at the final URL users can click.
  • Disk permissions. Queued workers often run as a different user than the web process; verify storage/app/exports (or your configured disk) is writable by the worker, not just by you.
  • Debugging. Log the resolved disk, path, row count, and elapsed time at the end of every export. When someone reports "the spreadsheet is missing rows", that single line usually tells you whether it was the query, the chunking, or the download.
  • Extension points. Before forking, check the package's hooks for custom format drivers and filename/path resolution — most "we need a slightly different header row" requirements are a subclass override, not a patch.
  • Upgrade hygiene. On any future 3.x → 4.x, re-read the exporter base class for signature changes; minor releases like 3.1.0 are safe, but the package's surface is small enough that a major bump will touch your subclasses directly.
Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
robertmarney/lara-hierarchial-collections
simonschaufi/rule-doc-generator
emissaoja/emissaoja
blackbricksoftware/gravityforms-civicrm-form-processor
al-rahabi/fancy-crud
jinas/dhivatar-component
mltstephane/laravel-quality
bernskiold/laravel-talentlms
bernskioldmedia/laravel-form-architect
youbar/easy-crud
c-point-labs/laravel-hostyourai
dbravoan/dbcms-admin-theme-dbcms
dbravoan/dbcms-theme-db-cyber
dbravoan/dbcms-blocks-basic
dbravoan/dbcms-module-content-audit
dbravoan/dbcms-module-ai-assistant
paygate-to/paygate-php
padosoft/askmydocs-connector-base
defstudio/supercharged-carbon
bernskioldmedia/laravel-matomo