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

mywheels/twig-spreadsheet-bundle

Twig Spreadsheet Bundle brings Twig templating to spreadsheet generation, letting you define workbook layouts in familiar templates and export structured data to spreadsheet files from your PHP or Symfony application.

View on GitHub
Deep Wiki
Context7

Getting Started

This is a Symfony bundle, not a Laravel package, and it is effectively abandoned (last release April 2019, one star, zero dependents). Treat it as a reference implementation rather than a dependency you install.

If you want to inspect it, clone the repo and look first at:

  • TwigSpreadsheetExtension.php — registers the Twig tags/DSL ({% spreadsheet %}, {% sheet %}, {% row %}, {% cell %}, style tags).
  • SpreadsheetFactory.php — the service that collects spreadsheet state and produces a PhpOffice\PhpSpreadsheet\Spreadsheet.
  • DependencyInjection/ and Resources/config/services.yml — Symfony wiring you would have to reproduce in a Laravel service provider.

Minimal install in a Symfony app (for context):

composer require mywheels/twig-spreadsheet-bundle phpoffice/phpspreadsheet

Register MyWheels\TwigSpreadsheetBundle\TwigSpreadsheetBundle::class in config/bundles.php.

In Laravel, the first practical use case is not this bundle — it's the concept: define export templates (reports, invoices, data dumps) that non-developers can edit without touching PHP. The direct Laravel equivalents are:

  • maatwebsite/excel (Laravel Excel) — the idiomatic wrapper around PhpSpreadsheet.
  • Or phpoffice/phpspreadsheet directly for one-off exports.

If you specifically want the "template-defined spreadsheet" idea, plan to port the DSL, not install the bundle.

Implementation Patterns

The bundle's pattern is a Twig DSL that mutates a shared Spreadsheet object as the template renders:

{% spreadsheet spreadsheet %}
    {% sheet 'Report' %}
        {% row %}
            {% cell %}Name{% endcell %}
            {% cell %}Amount{% endcell %}
        {% endrow %}
        {% for line in lines %}
            {% row %}
                {% cell %}{{ line.name }}{% endcell %}
                {% cell %}{{ line.amount }}{% endcell %}
            {% endrow %}
        {% endfor %}
    {% endsheet %}
{% endspreadsheet %}

In Symfony you'd typically inject SpreadsheetFactory, render the template, then call $factory->getSpreadsheet() and stream or save it. The Twig context can pass data in via $this->render('...', $data).

To reproduce the pattern in Laravel, wire Twig manually in a service provider:

$this->app->singleton(\Twig\Environment::class, function ($app) {
    $loader = new \Twig\Loader\FilesystemLoader(resource_path('spreadsheets'));
    $twig = new \Twig\Environment($loader, ['autoescape' => false]);
    // $twig->addExtension(new SpreadsheetExtension($app->make(SpreadsheetFactory::class)));
    return $twig;
});

Then wrap rendering in a job/service:

class SpreadsheetExporter
{
    public function __construct(private \Twig\Environment $twig) {}

    public function render(string $template, array $data): Spreadsheet
    {
        $spreadsheet = new Spreadsheet();
        $this->twig->render($template, $data + ['spreadsheet' => $spreadsheet]);
        return $spreadsheet;
    }
}

Better integration tips:

  • Queue large exports. PhpSpreadsheet is memory-hungry; run on a queue worker and write to storage, then hand the user a signed URL.
  • Separate template storage (resources/spreadsheets/) — don't mix with Blade views.
  • Favour a Blade directive if you'd rather not add Twig to a Laravel app: a custom @spreadsheetCell / @row directive can compile to the same builder calls with zero new template engine.
  • Cache the compiled Twig template — the DSL produces side effects at render time, so re-rendering for each row is the expected flow.

Gotchas and Tips

  • Not installable in a modern Laravel app as-is. The bundle requires Symfony 3/4-era framework-bundle and twig-bundle, and its composer.json pins early PhpSpreadsheet version ranges. Composer will refuse to resolve against Laravel 9/10/11 + current PhpSpreadsheet (2.x/3.x). Expect conflicts on psr/container, symfony/*, and phpoffice/phpspreadsheet.
  • No auto-discovery. Laravel's package discovery won't find a Symfony bundle. If you somehow force-install it, you must manually register its extension and factory — there is no service provider.
  • Twig is not the real problem; the abandonment is. One star, zero dependents, no commits in years, no security review. PhpSpreadsheet itself has had CVEs (XXE, zip-bomb DoS); pairing it with an old pinned version is risky for anything that parses user-supplied files.
  • Side-effectful rendering. The DSL works by mutating a shared object during render. If the template throws halfway (bad variable, divide-by-zero in a formula), you get a half-built spreadsheet. Wrap renders in try/catch if you ever port this pattern.
  • Style tags are the weakest part. Cell/row/column style declarations in the Twig DSL are verbose and hard to debug — there's no schema, so a typo silently produces an unstyled cell. If you port it, validate style keys against PhpSpreadsheet's Style object and log unknown keys.
  • Formulas. Anything computed (=SUM(...)) via the DSL requires getCalculatedValue() after render — a slow path on large sheets. Cache results or compute in PHP in the service layer instead.
  • Extension points are limited to re-implementing. There is no event system or "spreadsheet saved" hook. The practical extension point is the factory: swap it out and build the Spreadsheet yourself from a service that reads the template.
  • Migration recommendation: For a Laravel codebase, use maatwebsite/excel for exports, and if you want the template-driven experience, implement it as a custom Blade directive or a small Twig\Environment just for spreadsheet templates — either way, you keep the ergonomic DSL and drop the abandoned Symfony bundle entirely.
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.
frankyso/ipaymu
3mdikram/worldmoneywords
snowsoft/config
itmosoft/laravel-subscriptions
larva/laravel-ai-qwen
diephp/sequences
users-au/socialite-provider
twidpay/twid-logger
mrnewport/laravel-plaid
mrnewport/laravel-repo
mrnewport/laravel-docsign
mrnewport/laravel-stow
mrnewport/laravel-flow
mrnewport/laravel-priceable
mrnewport/laravel-nestedterms
fredmeng/laravel-apidoc-generator
itstructure/laravel-multilingual-tools
pdustdar/elasticsearch
markupai/php-sdk
rlacerda83/lumem-microservice-shipment