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

Excel Generator Bundle Laravel Package

rich-id/excel-generator-bundle

A Laravel/PHP bundle for generating Excel spreadsheets, offering a simple API to turn application data into downloadable .xlsx reports and exports. Composer-installable and suited to lightweight reporting workflows.

View on GitHub
Deep Wiki
Context7

Getting Started

The v1.0.2 release adds ExcelResponseFactory, the piece that turns a generated spreadsheet into an HTTP response. Previously you likely had to hand-roll response()->download(...) calls or rely on whatever Excel::download() exposed; the factory centralizes filename generation, content-type headers, and download-vs-inline behaviour.

Start here, in order:

  1. Check the service container binding. Most factories of this shape are registered as a singleton and are constructor-injectable. Look in the package's service provider for $this->app->singleton(ExcelResponseFactory::class, ...) or an alias. If it is bound, you never new it up.
  2. Read the factory's public methods. Expect something in the shape of download($export, string $filename, array $options = []), store(...), and possibly inline(...). The exact signatures matter more than the naming — check whether the first argument is an export class, a Spreadsheet instance, or a closure.
  3. Re-check the package README/UPGRADE notes for v1.0.2. The release note is one line; confirm whether existing Excel::download() calls are deprecated or merely supplemented.

First use case — the smallest thing that proves it works end to end:

// app/Http/Controllers/InvoicesExportController.php
public function __invoke(ExcelResponseFactory $excel)
{
    return $excel->download(new InvoicesExport($this->currentPeriod()), 'invoices-2024-06.xlsx');
}

Hit that route, confirm the browser receives an .xlsx with the right Content-Type and a sane Content-Disposition, and open the file in both Excel and LibreOffice before you build anything on top of it. A package that produces a file Excel repairs on open is worse than no package.

Implementation Patterns

Constructor injection in invokable controllers. The factory is stateless by design; inject it rather than resolving from the container inline. This keeps the controller testable and makes the dependency visible.

Factory as a formatting boundary. The best use of a response factory is to keep presentation decisions out of your export classes. Let the export class describe what the data is; let the factory own how it is delivered — filename, extension, headers, inline vs attachment. Put filename conventions (slugs, period suffixes, user prefixes) in one place, e.g. a small ExportName helper the factory consumes, instead of scattering sprintf() through controllers.

Wrap it in a response macro or your own thin service. If you have more than a handful of export endpoints, wrap the factory:

final class Exports
{
    public function __construct(private ExcelResponseFactory $factory) {}

    public function download(Exportable $export, string $basename, ?User $user = null): Response
    {
        $name = Str::slug($basename).'-'.now()->format('Y-m-d').'.xlsx';

        return $this->factory->download($export, $name, [
            'author'  => $user?->name,
            'company' => config('app.name'),
        ]);
    }
}

This gives you one choke point for auth checks, audit logging (Log::info('export', [...])), and future signature changes.

Pair downloads with queued generation for anything large. A response factory streams a file you already built; it does not make generation cheap. For exports over roughly 10–20k rows, generate to storage in a queued job and let the endpoint hand back a signed URL. Use the factory for the small, synchronous exports and a TemporaryUrl / Storage::download() path for the big ones. Keeping both paths behind the same wrapper above stops the two from drifting.

Testing pattern. Assert on the response envelope rather than on bytes:

$response = $this->actingAs($user)->get('/exports/invoices');

$response->assertOk()
    ->assertHeader('Content-Type', 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet')
    ->assertDownload('invoices-2024-06.xlsx');

For content assertions, write the response to a temp path and re-open it with the underlying reader — this catches column-order and date-format regressions that header assertions never will.

Integration with policies. Authorize before calling the factory, not inside it. A response factory should not be making authorization decisions; it has no request context and no way to fail cleanly.

Gotchas and Tips

  • Headers already sent. Any echo, dump(), dd(), or stray whitespace before the response returns will corrupt the download. Laravel's debug output and ray() calls are the usual culprits. Test on a route without middleware that prints anything.
  • Filename encoding. Non-ASCII names (accents, CJK) need RFC 5987 encoding in Content-Disposition. Check whether the factory does filename*=UTF-8''... or just quotes the raw string. If it does not, normalise to ASCII slugs in your wrapper rather than patching vendor code.
  • assertDownload() is loose. It compares the filename, not the contents. A factory that returns an empty or HTML-error body will still pass. Add one content-level test per export type.
  • Memory and execution time. The factory does not change generation cost. Watch for the classic Allowed memory size exhausted when an export class uses FromCollection with eager-loaded relations over a large table. FromQuery plus chunking is the fix; the factory is irrelevant to it.
  • Formula injection. If any cell can contain user-supplied text starting with =, +, -, or @, prefix with a single quote or force the cell to string type. This is a security issue, not a cosmetic one, and a response factory will happily ship the payload.
  • Date and number locale. PhpSpreadsheet stores dates as serials plus a format code. If you pass formatted strings instead, users lose sortability. Keep the raw Carbon instance and set the format code on the cell.
  • Temp file cleanup. Streaming from a temp file means the file must be unlinked after send. Verify the factory uses deleteFileAfterSend(true) (or the Symfony BinaryFileResponse equivalent); otherwise /tmp grows until the disk fills. This is the single most common defect in hand-rolled download responses.
  • Config publishing. Run php artisan vendor:publish --tag=<package>-config and read the published file before tuning anything — particularly driver selection, temp path, and any cache settings. Defaults are usually fine for local but wrong for multi-server setups.
  • Extension points to prefer. If the factory is not final, subclassing for a project-specific filename convention is tempting but brittle across patch releases. Prefer composition (the Exports wrapper above) and treat the factory as a dependency you do not modify.
  • Pin the version. ^1.0.2 is fine; a bare ^1.0 will pull in future minors that may alter method signatures on a factory you now call from every export endpoint.
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