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

os/excel-bundle

OS Excel Bundle integrates PHPExcel with Symfony to simplify reading, writing, and exporting Excel spreadsheets. Generate reports, stream downloads, and handle common spreadsheet formats from controllers and services with minimal configuration and boilerplate.

View on GitHub
Deep Wiki
Context7

Getting Started

1. Install (Composer, Symfony 2.x era). Note the package predates Composer autoloading conventions for many older bundles, so pin the version and verify the type in composer.json:

composer require os/excel-bundle

2. Register the bundle. In app/AppKernel.php:

$bundles = [
    // ...
    new OS\ExcelBundle\OSExcelBundle(),
];

3. Find the service first — this is the most important step. The bundle is a thin Symfony integration over PHPExcel, and its public surface is almost entirely its service definitions. Open:

  • Resources/config/services.xml (or .yml) — locate the service ID, usually something like os_excel / os_excel.excel, and inspect what class it wraps (expect PHPExcel or a factory).
  • DependencyInjection/OSExcelBundleExtension.php — confirms whether config parameters (format, cache path) exist.
  • Resources/doc/ — if present, it is usually the only documentation; if absent, the service XML is your API reference.

4. First use case — export a report as XLSX from a controller:

public function exportAction()
{
    $excel = $this->get('os_excel');            // confirm ID from services.xml
    $sheet = $excel->getActiveSheet();

    $sheet->setCellValue('A1', 'Order');
    $sheet->setCellValue('B1', 'Total');
    $sheet->fromArray($this->getDoctrine()->getRepository('AppBundle:Order')->findAll(), null, 'A2');

    $writer = $excel->createWriter($excel, 'Excel2007');   // wrapper-dependent
    $tmp = tempnam(sys_get_temp_dir(), 'xlsx');
    $writer->save($tmp);

    return new Response(file_get_contents($tmp), 200, [
        'Content-Type'        => 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
        'Content-Disposition' => 'attachment; filename="orders.xlsx"',
    ]);
}

If the bundle does not expose a writer factory, fall back to constructing PHPExcel_Writer_Excel2007 directly — the bundle will still give you the configured PHPExcel instance and its cache wiring.

Where to look first, in order: services.xml → the extension class → a sample controller if Tests/ or Resources/ contains one. With 0 dependents and 11 stars there is no ecosystem to learn from, so the source is authoritative.

Implementation Patterns

Inject the service, don't fetch from the container. In a real app you will export from more than one controller; wire a small domain service:

# services.yml
app.exporter.orders:
    class: AppBundle\Export\OrderExporter
    arguments: ['@os_excel']   # the bundle service ID
class OrderExporter
{
    private $excel;

    public function __construct($excel) { $this->excel = $excel; }

    public function build(array $orders)
    {
        $sheet = $this->excel->getActiveSheet();
        $sheet->setTitle('Orders');
        $sheet->fromArray(['Order', 'Total'], null, 'A1');
        $row = 2;
        foreach ($orders as $o) {
            $sheet->setCellValue("A{$row}", $o->getId());
            $sheet->setCellValue("B{$row}", $o->getTotal());
            $row++;
        }
        return $this->excel;
    }
}

Because PHPExcel workbooks are stateful and mutable, treat the injected instance as short-lived: build, write, discard. If the bundle defines the service as shared (it usually does), the same workbook will leak between requests/responses in long-running workers or when two exports run in one request. Either make the service non-shared (shared: false) or clone per export.

Template-driven reports (the highest-value pattern). For anything customer-facing, don't hand-build formatting in code. Ship an .xlsx with headers, styles, and formulas, load it, fill the data range, and write it back:

$excel = \PHPExcel_IOFactory::load($this->getParameter('kernel.root_dir').'/../data/order_template.xlsx');
$sheet = $excel->getActiveSheet();
$row = 3;
foreach ($orders as $o) {
    $sheet->setCellValue("A{$row}", $o->getReference());
    $sheet->setCellValue("B{$row}", $o->getTotal());
    $row++;
}
// Extend formulas/borders down to $row if your template has a "template row"

This is where the bundle earns its keep: it centralises the PHPExcel cache configuration (php://temp vs. filesystem), which otherwise causes "permission denied" temp-dir errors in production.

Streamed downloads for large exports. Do not build the whole file in memory and file_get_contents it if the report can exceed a few MB. Write to php://output and disable output buffering:

while (ob_get_level()) { ob_end_clean(); }
$writer = \PHPExcel_IOFactory::createWriter($excel, 'Excel2007');
$writer->save('php://output');

Return a StreamedResponse (Symfony 2.2+) or build the Response headers manually and exit from the controller (less clean but common in 2.x codebases).

Reading uploads. Symfony 2.x getRequest()->files is the only path here (the Request::files API is pre-Symfony 3). Validate extension and MIME, then:

$file = $request->files->get('import');
$excel = \PHPExcel_IOFactory::load($file->getRealPath());
$rows = $excel->getActiveSheet()->toArray(null, true, true, true);

Then push rows through a validator/import service — never persist directly from the sheet.

Console commands for scheduled reports. The killer app for this bundle is nightly exports. Create a ContainerAwareCommand, get the exporter service, write to a share/temp path, and mail or upload the file. This sidesteps HTTP timeouts entirely and is the pattern most teams actually ship.

Format strategy. Standardise on Excel2007 for anything users will open; use CSV (via PHPExcel_Writer_CSV) only when you need diffable output or a legacy ingest. Configure CSV delimiter/enclosure explicitly — PHPExcel defaults (; in some locales) bite.

Gotchas and Tips

This package is effectively unmaintained; budget for a migration. Symfony 2 is EOL and PHPExcel is abandoned in favour of PhpSpreadsheet (PHP 7.2+, namespaced classes). OSExcelBundle will not run unmodified on modern PHP. Use it only if you are maintaining a legacy Symfony 2 app, and isolate it behind your own exporter interface so a swap to PhpSpreadsheet touches one class.

Service ID is not guessable. There is no stable documented API; the container key depends entirely on services.xml. Run php app/console container:debug | grep -i excel to see what's actually registered before writing code against a guessed ID. This one step saves the most time.

Stateful service leakage. Shared PHPExcel instances accumulate sheets and cell values across calls. After each export, either $excel->disconnectWorksheets() or request a fresh instance. In tests using the same container across cases, this produces baffling "expected 1 row, got 4" failures.

Memory and the cache. PHPExcel is memory-hungry (cell-by-cell objects). A 20k-row export can exceed 128 MB. Set PHPExcel_Settings::setCacheStorageMethod (the bundle may expose this via config — check DependencyInjection/Configuration.php) to PHPExcel_CachedObjectStorageFactory::cache_to_sqlite3 or cache_to_phpTemp, and raise memory_limit in the command/controller. Also set PHPExcel_Settings::setZipClass(PHPExcel_Settings::PCLZIP) if the zip extension is missing.

"Headers already sent" / corrupt XLSX. Any stray whitespace, BOM, or var_dump before the writer flushes corrupts the archive. ob_end_clean() before writing to php://output, and never echo inside the export path. Corrupt files almost always mean output was emitted early — check headers_sent() in debug.

Dates and formulas. PHPExcel stores dates as serials; a raw float in a date-formatted cell renders as 45000. Use PHPExcel_Shared_Date::PHPToExcel(new \DateTime(...)) and apply a number format. Formulas written as =SUM(...) are not recalculated on save by default — call $excel->getCalculationEngine()->calculate() or set the writer's setPreCalculateFormulas(true) if downstream consumers need values.

Locale/encoding in CSV. Excel2007 handles UTF-8; CSV does not unless you prepend a BOM or set the writer to UTF-8 explicitly. Accented names become mojibake — a very common bug report.

No tests, no dependents, 11 stars. Treat every behaviour as undocumented. Read the source; do not trust blog posts about the package (there are none meaningful). Add your own integration test that asserts a generated file opens and contains expected cells — this is your only regression net.

Extension points are thin. There is no event system. To add behaviour (e.g. corporate styling, footer stamping), wrap the exporter service rather than trying to hook the bundle; the extension class typically only loads service definitions, so subclasses/decoration are the realistic customisation path.

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.
skyraptor/chatter
edlugz/sasapay
abbasudo/artisan-route
mirvan/ippanel
ignibyte/aic-framework
glavweb/data-schema-bundle
glavweb/datagrid-bundle
tacticmedia/scim-server
uni-method/frame
neika-ai/sdk
adachsoft/php-code-style
pharaonic/laravel-notification-dependencies
beautinow/translug
dev-brainiac/bot-cloaked
octoberrain/html
octoberrain/extension
octoberrain/exception
octoberrain/contracts
adachsoft/api-version-bundle
cgv/localized-resource