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 Bundle Laravel Package

onurb/excel-bundle

Symfony bundle that integrates PHPExcel to read and write Excel spreadsheets, exposing the library as a service with helpers for controllers and templates. Simplifies importing data from and exporting data to XLS/XLSX files in Symfony applications.

View on GitHub
Deep Wiki
Context7

Getting Started

  1. Install the wrapper plus its transitive PhpSpreadsheet dependency:

    composer require onurb/excel-bundle
    

    Check the resolved phpoffice/phpspreadsheet version against your PHP version — recent PhpSpreadsheet releases require PHP 8.1+, while older releases of this bundle were built for PHP 7.x. Pin versions defensively.

  2. Register the bundle (Symfony Flex will not do this for you, since there is no recipe):

    // config/bundles.php
    Onurb\Bundle\ExcelBundle\OnurbExcelBundle::class => ['all' => true],
    
  3. Find the factory service. The bundle's only real value is exposing PhpSpreadsheet behind a DI-managed factory:

    bin/console debug:container | grep -i excel
    

    Expect Onurb\Bundle\ExcelBundle\Service\Factory; older releases exposed it as excel.factory / PhpExcelBundle\Service\Factory. Autowire the class, not a string id, to be version-safe.

  4. First use case — a streamed export action:

    use Onurb\Bundle\ExcelBundle\Service\Factory;
    
    public function export(Factory $excel): Response
    {
        $spreadsheet = $excel->createSpreadsheet();
        $spreadsheet->getActiveSheet()
            ->setCellValue('A1', 'Name')
            ->setCellValue('B1', 'Total');
    
        $writer = $excel->createWriter($spreadsheet, 'Xlsx');
        return $excel->createStreamedResponse($writer); // sets headers + php://output
    }
    

The package is a thin adapter, so your first hour is better spent reading the PhpSpreadsheet docs than the bundle's README. The bundle adds: Symfony service wiring, a writer/reader factory, and a streamed-response helper. That's the entire leverage surface.

Implementation Patterns

Wrap the factory in your own exporter/importer service. Do not inject the bundle factory throughout your codebase; it couples controllers to a 13-star package. Create App\Excel\SpreadsheetExporter and App\Excel\SpreadsheetImporter, inject Factory once, and expose domain methods (exportInvoices(array $rows): StreamedResponse, importProducts(UploadedFile $file): array). If you later drop the bundle, only one class changes.

Export: build in memory, then choose sink.

$spreadsheet = $factory->createSpreadsheet();
$sheet = $spreadsheet->getActiveSheet();
$sheet->fromArray($headers, null, 'A1');
$sheet->fromArray($rows, null, 'A2');

// Sink A: browser download
return $factory->createStreamedResponse($factory->createWriter($spreadsheet, 'Xlsx'));

// Sink B: persist to disk (e.g., for Mailer attachment)
$factory->createWriter($spreadsheet, 'Xlsx')->save($tmpPath);

Use sink B for CLI commands and Messenger handlers; createStreamedResponse is only meaningful in an HTTP context.

Import: upload → reader → normalise.

$reader = $factory->createReader('Xlsx');
$reader->setReadDataOnly(true);
$spreadsheet = $reader->load($file->getPathname());
$rows = $spreadsheet->getActiveSheet()->toArray(null, true, true, true);

Always setReadDataOnly(true) for imports unless you need styling. Convert dates yourself — PhpSpreadsheet hands back Excel serial numbers:

$date = \PhpOffice\PhpSpreadsheet\Shared\Date::excelToDateTimeObject($value);

Unknown formats: use IOFactory::identify($path) then IOFactory::createReaderForFile($path) rather than trusting the file extension; this bypasses the factory but is far more robust for user uploads.

Format selection: the second argument to createWriter/createReader is the PhpSpreadsheet writer/reader short name — Xlsx, Xls, Csv, Ods, Html, Pdf. Case-insensitive, but standardise in a constant map in your wrapper. Pdf is not self-contained; see gotchas.

Batch/large files: use a IReadFilter (setReadFilter) to stream rows by range instead of toArray(), and for writes that exceed memory, write row-by-row with $writer->save('php://output') inside a StreamedResponse callback rather than building the full sheet.

Integration with Symfony Form uploads: accept UploadedFile, validate MIME/extension in a form constraint, then hand getPathname() to your importer. Never load a file the user hasn't passed through validation — PhpSpreadsheet parsing is a real attack surface (XXE, zip bombs in OOXML).

Gotchas and Tips

  • PDF writer needs a third-party renderer. createWriter($ss, 'Pdf') only works if one of dompdf/dompdf, mpdf/mpdf, or tecnickcom/tcpdf is installed and you pass the correct class. The bundle does not pull these in; expect \PhpOffice\PhpSpreadsheet\Writer\Pdf\... not-found errors otherwise. In practice most projects shell out to a headless browser or dedicated PDF lib instead.

  • Streamed response buffering. If you see "headers already sent" or a corrupted xlsx download, a stray echo, BOM, or active output buffer in your app is leaking into the stream. Nothing should output before the response; if you use a base controller with debugging, disable it for export routes.

  • Memory is the usual production failure. A 50k-row xlsx can exceed 256 MB. Mitigations, in order of impact: setReadDataOnly(true) on reads; setCache() on Settings with a PSR-16 cache backend; IReadFilter chunking; Writer::setPreCalculateFormulas(false) on writes; raise memory_limit in the export command only.

  • Dates, formulas, and nulls. toArray() returns null for empty cells and raw floats for dates. getCalculatedValue() returns the cached value unless the file was saved by Excel; if you generate a file and immediately re-read it, formulas may resolve to 0/null. Call $spreadsheet->getCalculationEngine()->calculate() or setPreCalculateFormulas(true) before saving.

  • CSV quirks. Default delimiter is , and encoding detection varies by locale. Set explicitly:

    $writer = $factory->createWriter($spreadsheet, 'Csv');
    $writer->setDelimiter(';')->setEnclosure('"')->setSheetIndex(0);
    

    A single-sheet CSV is fine; multi-sheet exports silently write only the active sheet.

  • Service-id churn across versions. Documentation online mixes excel.factory, PhpExcelBundle\Service\Factory, and Onurb\Bundle\ExcelBundle\Service\Factory. If autowiring fails, debug:container --show-arguments to confirm the real id — do not copy a service id from a blog post.

  • Extension points are sparse. There is no plugin system. If you need extra behaviour (array→sheet helpers, header styling, autosize), decorate the factory:

    final class SpreadsheetFactory
    {
        public function __construct(private Factory $inner) {}
        public function styledExport(string $title, array $rows): StreamedResponse { /* ... */ }
    }
    

    Register this as your own service and inject it everywhere; never subclass the bundle's factory (it is not designed for extension and internal signatures shift between releases).

  • Maintenance risk. 13 stars, zero known dependents, no Symfony recipe, and a wrapper that mostly forwards calls. Treat the bundle as optional sugar: PhpSpreadsheet's own API is stable and well-documented, and composer require phpoffice/phpspreadsheet with a small hand-written factory gives you the same result with fewer upgrade surprises. If you keep the bundle, isolate it behind your own exporter/importer services so swapping it out is a one-class change.

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