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

Twig Spreadsheet Bundle Laravel Package

erelke/twig-spreadsheet-bundle

Symfony bundle that integrates PhpSpreadsheet with Twig, letting you build spreadsheets from templates with macro and include support. Exports to XLSX, XLS, ODS, CSV, and PDF via mPDF.

View on GitHub
Deep Wiki
Context7

Getting Started

1. Check compatibility before installing. This bundle targets PHP 7.1+ and Symfony 3.4+ and its last release was April 2021. Before composer require, confirm your Symfony version is in the supported range (likely 3.4–5.x). On Symfony 6/7 you should expect to fork or patch the bundle (missing return types on Bundle subclasses, deprecated service/twig config).

composer require erelke/twig-spreadsheet-bundle
# PhpSpreadsheet is the core dependency; align it explicitly
composer require phpoffice/phpspreadsheet
# Only if you need PDF output
composer require mpdf/mpdf

2. Register the bundle — Flex: add to config/bundles.php; legacy: AppKernel::registerBundles().

3. Discover the real config and service IDs rather than trusting old blog posts:

bin/console config:dump-reference twig_spreadsheet
bin/console debug:container | grep -i spreadsheet

The bundle's service definitions under vendor/erelke/twig-spreadsheet-bundle/Resources/config/ and its tests are the most reliable documentation for Twig function names and the renderer service ID.

4. First use case — an "Export to Excel" button. Render a Twig template into a Spreadsheet object, then hand it to a PhpSpreadsheet writer:

#[Route('/export/orders.xlsx', name: 'export_orders')]
public function export(/* the bundle's renderer service */ $renderer): Response
{
    $spreadsheet = $renderer->render('spreadsheet/orders.xlsx.twig', [
        'orders'      => $orderRepository->findRecent(),
        'generatedAt' => new \DateTimeImmutable(),
    ]);

    $writer = new \PhpOffice\PhpSpreadsheet\Writer\Xlsx($spreadsheet);

    $response = new StreamedResponse(fn () => $writer->save('php://output'));
    $response->headers->set('Content-Type',
        'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet');
    $response->headers->set('Content-Disposition', 'attachment; filename="orders.xlsx"');

    return $response;
}

Verify the exact service ID and method signature from debug:container — the snippet above reflects the intended usage pattern, not a guaranteed API.

Implementation Patterns

  • Template-as-workbook. Each export is a Twig file that emits cells; keep one template per report shape (orders.xlsx.twig, invoices.pdf.twig). This is the bundle's whole value proposition: designers can tweak layout without touching PHP.

  • Macros for repeated rows. Lean on the bundle's macro/include support: a _header.twig macro for column titles, _row.twig for a data row, _totals.twig for aggregates. Compose per report rather than copy-pasting markup.

    {% import 'spreadsheet/_row.twig' as row %}
    {% for order in orders %}
        {{ row.render(order) }}
    {% endfor %}
    
  • Use an existing .xlsx as the styling template. Instead of fighting PhpSpreadsheet's style API from Twig, keep a master workbook with fonts, colors, column widths, and number formats, load it as the base, then fill cells. This is the most maintainable path for anything client-facing.

  • Writer selection by format. Centralize format → writer mapping (Xlsx, Xls, Ods, Csv, Mpdf) in one service so controllers stay thin and Content-Type/filename headers stay consistent.

  • Async for anything large. Render in a Messenger handler and store to disk/S3, then email a link. Streaming a 50k-row workbook from a web request will time out.

  • Cache/refactor into an ExportService. Wrap renderer + writer + response-building so a controller is 3 lines and exports are unit-testable.

  • Testing. Don't snapshot bytes. Re-read the produced file with IOFactory::load() and assert on cell values/formulas — the workbook is the contract.

Gotchas and Tips

  • Abandonment risk is the headline gotcha. 2 stars, 0 dependents, last release 2021, no Symfony 6+ support. Treat it as vendored glue code: pin the version, and have a fallback plan (plain PhpSpreadsheet + a small Twig renderer) for your next major upgrade. The conceptual design is easy to replicate; the lock-in is not worth fighting a dead bundle.
  • PHP 8 deprecations. Code written for PHP 7.1 will emit deprecation notices under 8.1+. Pair it with a modern phpoffice/phpspreadsheet and expect to patch signatures.
  • Memory. PhpSpreadsheet loads whole workbooks into memory. For big reports: raise memory_limit, enable cell caching, use setReadDataOnly when reading templates, and prefer CSV for high-volume data pipelines.
  • CSV/formula injection. Twig renders values as text, but a value starting with =, +, -, or @ becomes a formula or an injection vector. Prefix user-supplied strings with ' or sanitize before writing.
  • Twig whitespace leaks into cells. Newlines and indentation around {% %} blocks can land in cell values or shift your layout. Use {%- -%} trimming in spreadsheet templates.
  • Dates and numbers. Pass DateTimeInterface objects, not formatted strings, and set number formats in the base template so Excel localizes correctly. For CSV, set delimiter/enclosure explicitly for your target locale (decimal comma, etc.).
  • Debugging is manual. In dev, save the rendered workbook to var/ and inspect with getCell('A1')->getValue(). Twig exceptions inside a spreadsheet template are often opaque — isolate one template at a time.
  • PDF is the weakest path. mPDF needs font configuration; CJK/emoji require extra fonts, and PhpSpreadsheet's PDF export is limited. Prototype PDFs early — consider rendering HTML→PDF directly if fidelity matters.
  • Config quirks. Default format and template base paths are configurable; re-check config:dump-reference twig_spreadsheet after any upgrade rather than assuming the old YAML still applies.
  • Extension points. Custom Twig functions/filters (e.g., a currency formatter) go in a normal AbstractExtension; custom writers or a replacement renderer can override the bundle service. These are your escape hatches when the upstream code stops fitting.
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.
elmogy/larafast
xeviant/laravel-paystack
gorilladash/laravel-website-sdk
martialbe/flysystem-oss
chondal/3312client
jeroen-g/flickr
opscale-co/nova-genbi
itsjustvita/laravel-retention
tequilarapido/trackit
gregsphotography/site-builder-filament
cabaoctavio/laravel-query-log
gregsphotography/site-builder-components
io-digital/clickatell
snowsoft/laravel-admin
oxygen/auth
relicsoftware/barakah-payment-gateway
tobya/work-with-composer
equipc/laravel-email-validator
equipc/laravel-email-database-log
adedsec/nova-translated-labels