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

Spout Bundle Laravel Package

stev/spout-bundle

A PHP bundle wrapping the Spout spreadsheet library, giving your app a simple service for importing and exporting XLSX, ODS, and CSV files. Handy for reading and writing spreadsheets without heavy dependencies or extension requirements.

View on GitHub
Deep Wiki
Context7

Getting Started

  • Install and register. composer require stev/spout-bundle, then add the bundle to config/bundles.php. Confirm the exact FQCN by reading vendor/stev/spout-bundle/src/ before editing — the class name is not documented anywhere reliable, and this package has no README worth relying on.
  • Check compatibility first. The bundle targets Symfony 4/5 and Spout v3. Run composer why-not stev/spout-bundle and inspect its composer.json (php, symfony/*, box/spout constraints) before trying to drop it into a Symfony 6/7 project. Expect to pin, or to skip the bundle entirely.
  • Read the container, not the docs. The bundle is a thin wiring layer over Box\Spout\Reader\Common\Creator\ReaderEntityFactory and Box\Spout\Writer\Common\Creator\WriterEntityFactory. Run php bin/console debug:container | grep -i spout and open vendor/stev/spout-bundle/src/Resources/config/services.* to see the services/tags it actually exposes. That five-minute read is the whole "documentation" for this package.
  • First use case: a one-shot CSV or XLSX import in a console command. Open a reader, iterate rows, map headers, flush to Doctrine in batches. That is the highest-value, lowest-risk way to start — it keeps Spout's streaming model intact and avoids the browser-download edge cases (see Gotchas).
// Inside a Symfony command — pattern, not a bundle-specific API
$reader = ReaderEntityFactory::createReaderFromFile($path); // CSV, XLSX, ODS
$reader->open($path);
foreach ($reader->getSheetIterator() as $sheet) {
    foreach ($sheet->getRowIterator() as $i => $row) {
        if ($i === 1) { $headers = $row->getCells(); continue; }
        // map + persist
    }
}
$reader->close();

Implementation Patterns

  • Import service per file type. Wrap the reader in a small App\Import\XlsxImporter that accepts a header map and a row callback, so controllers and commands share one code path. Let the bundle (or the raw Spout factory) construct the reader; keep iteration logic in your service.
  • Batch the database work, not the reading. Spout streams one row at a time, so memory stays flat — don't break that with iterator_to_array($sheet->getRowIterator()). Instead flush Doctrine every ~500 rows and $em->clear() to keep the identity map from growing.
  • Exports: pick a target deliberately.
    • Command / queued job: WriterEntityFactory::createXLSXWriter()->openToFile($path), add rows, close(), then email or move the file.
    • HTTP download: open the writer inside a StreamedResponse callback with openToBrowser($filename) and close(). This is the only clean way to combine Spout's output with Symfony's response lifecycle.
  • Build cells, not strings. WriterEntityFactory::createRowFromArray($values) is fine for plain data, but use WriterEntityFactory::createCell(...) when you need dates, numbers, or formulas to land in the right Excel type — otherwise everything arrives as text and users complain.
  • Validate before writing/after reading. The bundle adds no validation. Put a thin layer in front of the importer (column count, required headers, type casting) so bad spreadsheets fail with a useful message instead of a TypeError from Spout internals.
  • Isolate the dependency. Because this package is unmaintained, wrap all Spout usage behind your own interfaces. If you later swap the bundle for box/spout v4 (different factories), only your adapter layer changes.

Gotchas and Tips

  • The license is the headline problem. The bundle is GPL-3.0. Spout itself is Apache-2.0 (permissive), but this wrapper is copyleft. For a commercial/proprietary application, shipping GPL code in-process is a legal question most teams aren't prepared to answer. The safer move for most projects: install box/spout directly (or its successor openspout/openspout) and skip the bundle.
  • Effectively abandoned. Last release September 2020, zero dependents. No Symfony 6/7 support, no maintenance, and Spout v3's ReaderEntityFactory/WriterEntityFactory were removed/renamed in v4. You are pinning an old Spout version — audit it for CVEs before adopting.
  • openToBrowser() fights Symfony. It emits Content-Type/Content-Disposition and exits-adjacent behavior through output buffering. If you call it from a normal controller action you'll get corrupted responses or "headers already sent". Always use StreamedResponse and call ob_end_clean() if anything echoed earlier.
  • Temp files. Spout v3 writes intermediate files to sys_get_temp_dir(). On containers with a small /tmp (or read-only mounts) large exports silently fail. Set a writable temp path and monitor disk.
  • Memory looks fine until it isn't. Readers stream, writers buffer less than you'd think, but $sheet->getRowIterator() inside foreach is lazy — don't collect rows into arrays "to sort them". Stream and sort downstream.
  • Date/time handling. Excel dates come back as numeric serials or DateTime objects depending on the file. Normalize explicitly; don't strtotime() a float.
  • Debugging entry points. debug:container --tag=..., reading the bundle's service definitions, and stepping through Spout's ReaderFactory are far more productive than searching for docs. If a reader type isn't resolving, the cause is almost always the file extension → reader mapping, not the bundle.
  • Extension point worth knowing. If the bundle exposes a factory service, prefer injecting that over calling the static *EntityFactory methods in your code — it keeps you testable and makes the eventual migration to Spout v4 a one-file 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.
uni-method/symfony-lib
tacticmedia/scim-server-bundle
neika-ai/symfony-bundle
mattleads/agent-publishing-bundle
glavweb/framework-bundle
glavweb/extra-bundle
glavweb/api-bundle
glavweb/admin-bundle
adachsoft/api-version-bundle
elsayed85/laravel-api-exceptions
ouhssini/laravel-demo-mode
daworks/ncloud-outbound-mailer-driver
gp247/cms
ignibyte/aic-gates
nexa-merchant/blog
novasa/laravel-language-center
opencage/geocode
adolfoholzer/parameters
livenetworks/ln-starter
cgv/localized-resource