mattfanz/twig-spreadsheet-bundle
A Symfony bundle that brings spreadsheet generation into Twig templates, letting you build and render Excel-style documents directly from your views with minimal PHP boilerplate. Useful for exports, reports, and downloadable data files.
Read this first: it is a Symfony bundle, not a Laravel package. With 1 star, 0 dependents, and a near-zero adoption score, treat it as a reference implementation or a short-lived dependency rather than a foundation. Before writing any code, spend ten minutes on the repo: check the last commit date, the composer.json constraints (symfony/framework-bundle, phpoffice/phpspreadsheet, PHP version), whether there are tags/releases, and whether the README’s API matches the code in src/. If the constraints exclude your Symfony/PHP version, stop and vendor the useful classes instead.
Minimal steps in a Symfony app:
composer require mattfanz/twig-spreadsheet-bundle (pin an exact version; there is no ecosystem pressure keeping it compatible).config/bundles.php (new MattFanz\TwigSpreadsheetBundle\TwigSpreadsheetBundle() — confirm the real FQCN in the repo’s src/), or let Flex do it if a recipe exists (unlikely).DependencyInjection/ and Resources/config/ to learn the actual service IDs and configuration tree. Do not trust the README alone; the service definitions are the source of truth.bin/console debug:container --show-private | grep -i spreadsheet and debug:config for the bundle alias to see what you actually got.bin/console lint:twig templates/ to confirm the Twig extension is registered.Where to look first: the Twig extension class (usually Twig/Extension/*Extension.php) tells you the exact function/filter names and signatures. That is your real API documentation. The bundle’s factory/service is what you inject into controllers or exporter services.
First use case: a controller that builds a workbook, streams it as an .xlsx response, and uses a Twig template for headers/labels so non-developers can tweak column titles. Confirm the round trip works end to end (open the file in LibreOffice, not just Excel) before building anything larger.
If you are actually working in Laravel: you cannot register a Symfony bundle in a Laravel kernel. Either copy the Twig extension and factory classes into app/Support/ and wire them manually, or skip this package and use phpoffice/phpspreadsheet directly (or maatwebsite/excel). The bundle gives you almost nothing beyond service wiring and a few Twig functions, and you can reproduce that in an hour.
Inject the factory, keep templates dumb. The clean pattern is a dedicated SpreadsheetExporter service that receives the bundle’s factory/workbook service, builds sheets programmatically, and only delegates labels and small layout decisions to Twig. Do not try to express workbook structure (merged ranges, formulas) in Twig; it becomes unreadable and untestable fast.
Controller → StreamedResponse. Build the workbook, write to php://output via PhpSpreadsheet\Writer\Xlsx, and return a StreamedResponse with Content-Type: application/vnd.openxmlformats-officedocument.spreadsheetml.sheet and a Content-Disposition filename. Set ob_end_clean()/flush() carefully; stray output corrupts the binary.
Twig-assisted generation. Where the bundle exposes a render-to-spreadsheet path, the workflow is: render a Twig template to a string, parse the rows (CSV/HTML/whatever the bundle supports), and hand them to PhpSpreadsheet. Use it for report headers, footers, and translation strings — not for the data loop. Verify what the Twig functions actually return before designing around them.
Reusable exporter services. Define an interface (SpreadsheetExporterInterface::export(ReportData $data): Workbook), one implementation per report, and tag them. This lets you swap the bundle out later without touching controllers — important given the maintenance risk.
Queue large exports. Anything over a few thousand rows should go through a messenger job that writes to storage and emails a link. PhpSpreadsheet holds the whole workbook in memory; a synchronous request will time out or exhaust memory_limit.
Integration tips. Check whether the bundle autowires aliases; if not, bind the concrete service ID in services.yaml. Use %kernel.cache_dir% or a dedicated temp directory for any intermediate files, and make it configurable via the bundle config or your own parameter. In tests, assert on cell values via $sheet->getCell('A1')->getValue() rather than byte-comparing files.
src/ and delete the dependency.debug:container and cache:clear are your first diagnostics; a ServiceNotFoundException usually means the bundle wasn’t registered or the config key is wrong.{%- -%} trimming and test with a tiny fixture.&) may leak into cells if data is rendered through Twig. Disable escaping for data values.setPreCalculateFormulas(true) on the writer.--memory-limit=-1 for CLI exports and consider Settings::setCache for large workbooks. Xlsx is the heaviest writer; CSV is far cheaper if the consumer doesn’t need formatting.echo, warning, or BOM before the writer runs produces a file Excel refuses to open. Wrap generation in output buffering and log instead of printing.composer.json expecting services; you’ll get classes but no wiring. Copy the Twig extension and register it with Laravel’s Twig bridge (if you use one), or just use PhpSpreadsheet with Blade.debug:container for the service, lint:twig for the extension, a one-cell fixture for the writer, and LibreOffice for the file itself. If all four pass, the problem is your data, not the bundle.How can I help you explore Laravel packages today?