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

Nova Queued Export As Csv Laravel Package

nova-kit/nova-queued-export-as-csv

Adds queued CSV exports to Laravel Nova resources, so large datasets export asynchronously through background jobs instead of timing out or blocking the UI. Useful for admin panels needing reliable, scalable data exports.

View on GitHub
Deep Wiki
Context7

Getting Started

Install and wire it up once, then treat it as a standard Nova action.

composer require nova-kit/nova-queued-export-as-csv
php artisan vendor:publish --tag=nova-queued-export-as-csv-config

The package pulls in maatwebsite/excel transitively and assumes you have a real queue worker (QUEUE_CONNECTION=database|redis, not sync) plus a writable filesystem disk. Publish the config first — it is where disk, queue, and the notification class live, and you will want to touch it on almost every project.

Where to look first, in order:

  1. config/nova-queued-export-as-csv.php — disk, queue name, notification class. This answers 80% of "why didn't it work" questions.
  2. src/QueuedExportAsCsv.php — the action you actually put in actions(). Read the public setters (columns(), filename(), etc.) before you invent your own.
  3. src/Jobs/* — what runs on the worker. This is where chunking, querying, and file writing happen.
  4. src/Notifications/* — the "your export is ready" class, usually with a link or file path.
  5. README.md — the action API changes between releases; pin a version and read the README for that tag.

First use case — prove the loop on one resource with 10 rows:

// app/Nova/User.php
use NovaKit\NovaQueuedExportAsCsv\QueuedExportAsCsv;

public function actions(NovaRequest $request): array
{
    return [
        ...parent::actions($request),
        new QueuedExportAsCsv,
    ];
}

Then run php artisan queue:work, hit the action from the Nova index, and verify three things in this order: (1) the job appears in jobs and is consumed, (2) the CSV lands on the configured disk, (3) the Nova notification appears in the bell menu. If any of those three is missing, you know which layer to debug.

Implementation Patterns

Gate it like any other action, not with if inside the job.

(new QueuedExportAsCsv)
    ->canSee(fn (NovaRequest $request) => $request->user()->can('export', User::class))
    ->canRun(fn (NovaRequest $request, User $user) => $request->user()->can('export', $user));

The queued job runs outside the request, so canRun is your last line of authorization before dispatch. Do not rely on the resource's indexQuery scoping alone.

Explicit columns beat inferred columns. When the package derives columns from the resource's index fields, any non-database field (accessor, relationship, computed column) either fails the query or comes out empty. Map explicitly and keep the list aligned with what the resource actually exposes:

(new QueuedExportAsCsv)
    ->columns(['id', 'name', 'email', 'created_at'])
    ->filename('users-'.now()->format('Ymd-His').'.csv');

(Setter names follow the current release; confirm against src/QueuedExportAsCsv.php for your pinned version.)

Reuse via a trait or a shared base action. In a real app you'll attach this to 5–10 resources. Put the policy check, filename convention, and column mapping in one place:

trait QueuedCsvExportAction
{
    protected function csvExportAction(): QueuedExportAsCsv
    {
        return (new QueuedExportAsCsv)
            ->canSee(fn ($request) => $request->user()->can('exportCsv', static::class))
            ->filename(class_basename(static::class).'-'.now()->format('Ymd-His').'.csv');
    }
}

Push exports to a dedicated queue. CSV generation for 100k rows will occupy a worker for minutes. Set the queue in config to exports and run a separate supervisor pool so an export never blocks password-reset emails.

Respect Nova filters, not just the visible page. Nova passes the current filters on the request; if the action only serializes $models, you export the current page rather than the filtered set. For "export everything matching the filter" behaviour, rebuild the query from the request payload inside the job (resource class + filters + search) rather than passing an Eloquent collection through the queue.

Test the job in isolation. Faking the queue only proves dispatch happened; it does not prove the CSV is correct.

Queue::fake();
// hit the action / call it directly
Queue::assertPushed(ExportAsCsvJob::class);

// separately, with Storage::fake('local') and a small factory set:
(new ExportAsCsvJob(...))->handle();
Storage::disk('local')->assertExists($expectedPath);

If the package dispatches a Laravel Excel export class, you can also subclass that export and bind your version in the container — that is the cleanest extension point when you need custom formatting, date handling, or chunking.

Notification as the UX. The action's value is "fire and forget." Make sure the notification's via() includes database so it lands in Nova's notification centre, and that any download link is either a temporaryUrl() (S3) or served through a signed route (local disk). Never expose storage/app directly.

Gotchas and Tips

  • QUEUE_CONNECTION=sync silently defeats the entire package. The export runs in the request, times out on large datasets, and the "queued" notification fires immediately. This is the #1 local-vs-prod surprise.
  • No worker, no file. The action returns success and a "queued" toast even when nothing is consuming the queue. Check jobs table / Horizon before blaming the package.
  • Serialized models go stale. If the job carries Eloquent models, a record deleted between dispatch and execution can fail the whole export. Prefer IDs + re-query inside the job, and guard against null rows.
  • Memory blows up on wide exports. CSV writing itself streams, but if the underlying export uses FromCollection or a full get(), PhpSpreadsheet-style memory behaviour still applies. Use FromQuery with chunk()/cursor(), and sanity-check with 50k rows before shipping.
  • Soft deletes and global scopes. The export follows the resource's indexQuery, which means trashed records are excluded unless the user selected a trashed filter. Verify this is what finance actually wants before the first "missing rows" ticket.
  • CSV injection is a real risk. Any cell starting with =, +, -, or @ can execute in Excel. Prefix with ' or a tab in a custom export class if users upload free-text fields.
  • Excel + UTF-8 = mojibake. Add a BOM (\xEF\xBB\xBF) when the audience opens CSVs in Excel on Windows. Non-issue for Google Sheets / pandas.
  • Filename collisions. Two admins exporting at 14:03 will overwrite each other on a shared path. Include user ID or a UUID in the filename.
  • Disk visibility. local disk files aren't web-accessible; public disk needs php artisan storage:link. S3 needs temporaryUrl support and correct bucket policy.
  • Config cache. After changing disk or queue in the config, php artisan config:cache (or clear it) — stale cached config is a common "I changed it and nothing happened" moment.
  • Timeouts and retries. Set the job's $timeout below your worker's --timeout, and retry_after above it. A failed export with no notification is indistinguishable from a slow one to the end user; add a failure notification if the package doesn't ship one.
  • Debugging checklist: php artisan queue:failed, storage/logs/laravel.log, Horizon's failed tab, and drop the queue connection to sync locally only to get a stack trace in the Nova UI.
  • Maintenance risk. This is a small community fork (21 stars, one maintainer, last release Feb 2025) of a package whose upstream has churned. Pin the version in composer.json, review the diff before each Nova major upgrade, and be prepared to vendor the ~5 files into your app if it stalls. The surface area is small enough that forking is a realistic escape hatch.
  • Extension points worth knowing: the action class (subclass and override filename/columns), the job (override the query), the export class (bind your own in the container), and the notification class (swap via config). That's four override seams — enough to adapt without patching vendor code.
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.
jinas/dhivatar
store-doc/laravel-storage-driver
plus-it/laravel-translation-manager
diephp/perhaps
diephp/telegraph
osmaviation/spreadsheet
microsoft/fluentui-system-icons
mmccook/php-json-canonicalization-scheme
middlewares/firewall
qubiqx/montapacking
nyoncode/laravel-package-console
pnscripts/filament-broadquill-lite
pnscripts/laravel-sentfold-lite
vleap/mx-sdk-laravel-internal
twidpay/utils
htetmyathlaing/scout-elasticsearch-driver
artisan-toolbox/front-awesome
ls/client-assistant
users-au/laravel-client
cgv/localized-resource