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

Download Operation Laravel Package

backpack/download-operation

Adds a Download operation to Backpack for Laravel CRUD panels, letting admins export filtered table data to CSV or Excel with configurable columns, headers, and button options—ideal for quick reporting without custom export code.

View on GitHub
Deep Wiki
Context7

Getting Started

  1. Install alongside Backpack CRUD (this package is an operation, not a standalone downloader):

    composer require backpack/download-operation
    

    It assumes backpack/crud is already installed and working in your admin panel.

  2. Look at the trait first. Open vendor/backpack/download-operation/src/.../Operations/DownloadOperation.php (or wherever the service provider registers it) — the README is short and the trait is the real documentation. Confirm the exact FQCN your version uses for the use ...DownloadOperation; import before you paste it.

  3. Add it to the CRUD controller you want a download button on:

    use Backpack\DownloadOperation\Operations\DownloadOperation; // verify FQCN
    
    class InvoiceCrudController extends CrudController
    {
        use \Backpack\CRUD\app\Http\Controllers\Operations\ListOperation;
        use \Backpack\CRUD\app\Http\Controllers\Operations\ShowOperation;
        use DownloadOperation;
    
        protected function setupDownloadOperation()
        {
            // configure the callback / filename / disk here
        }
    }
    
  4. First use case — Download Invoice PDF. The button appears in the line stack (and, depending on version, on the Show page). Point the operation at your already-generated PDF (e.g. storage/app/invoices/{id}.pdf) and return a response()->download(...) from the callback. That's 90% of real-world usage.

  5. Sanity check with php artisan route:list | grep download — you should see a new route for the entity. Hit it while logged in as an admin and confirm the file lands in ~/Downloads.

Implementation Patterns

File already on disk (offline-generated invoices, signed contracts, exports):

protected function setupDownloadOperation()
{
    $this->crud->setOperationSetting('downloadCallback', function ($entry) {
        $path = storage_path("app/invoices/{$entry->id}.pdf");
        abort_unless(file_exists($path), 404);
        return response()->download($path, "invoice-{$entry->number}.pdf");
    });
}

Generate on the fly (dompdf, snappy, spatie/laravel-pdf):

$this->crud->setOperationSetting('downloadCallback', function ($entry) {
    $pdf = Pdf::loadView('invoices.pdf', ['invoice' => $entry]);
    return $pdf->download("invoice-{$entry->number}.pdf");
});

This is where the package shines — the operation handles the button, the route, the auth middleware and the response plumbing; you just supply the binary.

Dynamic filename from entry attributes:

$this->crud->setOperationSetting('downloadCallback', function ($entry) {
    $name = sprintf('invoice-%s-%s.pdf', $entry->number, now()->format('Ymd'));
    return response()->download($entry->getPdfPath(), $name);
});

Never use raw user input for the filename or path — sanitize and use the entry's stored, trusted identifiers.

Gate / ownership checks: Backpack operation middleware gives you admin auth, but not per-entry ownership. Enforce it in the callback:

abort_unless(auth()->user()->can('view', $entry), 403);

This also doubles as the place to log who downloaded what (audit trail) — a very common requirement for invoices.

Multiple entities: add the trait to each CRUD controller. Because it's an operation, you can mix it into a base CrudController you extend from, keeping the callback in a shared service that switches on $entry::class.

Combining with Show / List: the button is usually injected automatically. If your version doesn't, add it explicitly in setupListOperation / setupShowOperation via $this->crud->addButtonFromView('line', 'download', 'download', 'end') or the operation's helper.

Streaming vs downloading: for large files prefer response()->streamDownload(...) inside the callback; the operation just passes the response through, so anything Laravel's ResponseFactory returns works.

Testing: use Storage::fake('invoices'), call the download route with actingAs($admin), and assert assertDownload('invoice-123.pdf'). The callback makes this trivial since you can swap the file path via config in tests.

Gotchas and Tips

  • FQCN drift / low-maintenance package. 13 stars and a small maintainer surface means the README can lag behind the code. When the trait fails to resolve, check composer show backpack/download-operation and inspect the actual namespace before assuming the docs are wrong — the namespace convention for Backpack add-on operations is the usual culprit.

  • Operation must be registered, not just used. If the button doesn't appear but the route does, it's almost always that you've added the trait but not called the setup method, or your routes/backpack/custom.php is customising the CRUD route group and excluding it. php artisan route:list is your fastest diagnostic.

  • You must return the response. The downloadCallback (or whatever it's named in your version) expects a Symfony Response. Writing to output with echo/readfile inside the callback produces a corrupted file — always return response()->download(...) or ->streamDownload(...).

  • File-not-found → ugly 500. A missing invoice PDF will 500 unless you abort(404) or abort_if(! file_exists(...)). Users clicking "Download" on an unpaid invoice and getting a stack trace is the most common support ticket for this feature.

  • Authorization is not automatic per entry. Backpack's operation middleware only checks that the current admin can access the CRUD, not that they own the record. Always add a Gate/policy check in the callback for anything sensitive (invoices, contracts, PII). This is the #1 security pitfall in download operations.

  • S3 / non-local disks need the right response method. response()->download($path) expects a local filesystem path; for S3 use response()->streamDownload(fn () => readfile($url), $name) or stream from Storage::disk('s3')->readStream($key). Mixing the two silently returns a 0-byte file.

  • Content-Disposition and MIME. If the browser opens the file inline instead of downloading, you forgot the filename argument or the headers. response()->download($path, $name, ['Content-Type' => 'application/pdf']) is the safest form for PDFs.

  • Large PDFs and PHP limits. Generating PDFs on the fly can blow memory_limit/max_execution_time for big invoices. Prefer pre-generated files for high-volume entities, or queue generation and download the stored artifact.

  • Don't trust the URL. If the route is /admin/invoices/{id}/download, an admin (or an IDOR from a poorly scoped gate) can enumerate entries. Combine route model binding with a policy — never fetch by request()->input('id') inside the callback.

  • Extending it. Because it's just an operation trait, the cleanest customisation is to override the setup method in a base controller, or add a DownloadService that maps $entry::class to a generator. Forking the package is rarely necessary — subclass the controller instead.

  • Version pin. The last release date is recent but the package is young; pin it in composer.json and re-read the trait after composer update backpack/*, since Backpack operations occasionally change their setting keys between minor versions.

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.
coleus/frame
thtg88/journalism
filagora/filagora-ai-forums
rlacerda83/query-parser
socialdept/atp-orm
cboxdk/laravel-assistant-client
socialdept/atp-resolver
socialdept/tenant-domains
tautvydasr/blade-class-names
socialdept/color-scales
maherelgamil/controller
coleus/widgets
coleus/table
coleus/users
coleus/music
coleus/health
coleus/calendar
coleus/notes
coleus/support
coleus/settings