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

Spreadsheet Laravel Package

portphp/spreadsheet

Spreadsheet reader and writer for PortPHP, built on PhpSpreadsheet. Import Excel and CSV files into PHP applications and export data to XLSX, ODS, or CSV through a consistent, extensible API.

Deep Wiki
Context7

Getting Started

portphp/spreadsheet is a thin PortPHP adapter around PhpSpreadsheet: it gives you a spreadsheet-backed reader/writer so Excel files can participate in PortPHP workflows instead of hand-writing IOFactory loops. Install it with Composer; it pulls in phpoffice/phpspreadsheet and the PortPHP core interfaces.

The fastest first use case is “read an uploaded XLSX/ODS/XLS file as associative arrays keyed by the header row”:

use Port\Spreadsheet\Reader\SpreadsheetReader;
use Port\Workflow;

$reader = new SpreadsheetReader(__DIR__ . '/orders.xlsx');
$reader->setHeaderRowNumber(0);        // zero-based header row
// $reader->setSheetName('Orders');    // if reading a specific sheet
// $reader->setFirstDataRowNumber(1);  // override automatic data start

$workflow = new Workflow();
$workflow->addReader($reader);
$workflow->addWriter(new class implements \Port\Writer\WriterInterface {
    public function prepare(): void {}
    public function writeItem(array $item): void { var_dump($item); }
    public function finish(): void {}
});

$workflow->process();

You can also iterate the reader directly (foreach ($reader as $row)) when you do not need PortPHP’s filters/converters/writers.

Where to look first:

  1. The package README/example for the exact constructor and setter names in your installed version.
  2. SpreadsheetReader::getHeader() and the first few rows of a real file; this exposes header and type quirks immediately.
  3. PortPHP core docs for Workflow, FilterInterface, ConverterInterface, and WriterInterface.
  4. PhpSpreadsheet’s IOFactory and Reader\Xlsx docs, because most surprising behavior comes from PhpSpreadsheet, not this adapter.

Use portphp/csv instead when the input really is plain CSV and files are large; this package is most useful when you need XLSX/ODS/XLS support or multi-format detection.

Implementation Patterns

Validated import pipeline. The normal pattern is reader → converters → filters → domain writer. Converters run before filters in a PortPHP workflow, so normalize values first, then validate them:

$workflow->addConverter(new class implements \Port\Converter\ConverterInterface {
    public function convert($item) {
        $item['total'] = (float) str_replace(',', '', $item['total']);
        $item['ordered_at'] = new \DateTimeImmutable($item['ordered_at']);
        return $item;
    }
});
$workflow->addFilter(new class implements \Port\Filter\FilterInterface {
    public function filter($item): bool {
        return $item['total'] > 0 && $item['email'] !== '';
    }
});

For persistence, write a small batching writer that buffers N rows and flushes via Doctrine/PDO/queue. Do not use one INSERT per spreadsheet row.

Database export to XLSX. Use a generator as the reader and let the spreadsheet writer consume arrays:

$reader = (function () use ($pdo) {
    foreach ($pdo->query('SELECT id, email, total FROM orders') as $row) {
        yield $row;
    }
})();

$writer = new SpreadsheetWriter(__DIR__ . '/export.xlsx');
// If your version exposes header control:
$writer->setHeader(['id', 'email', 'total']);

$workflow = new Workflow();
$workflow->addReader($reader);
$workflow->addWriter($writer);
$workflow->process();

Multi-sheet workbooks. A reader represents one sheet at a time. For an import of several sheets, create a reader per sheet name (or reset the sheet and rewind between passes) and run separate workflows with sheet-specific converters/writers. Avoid expecting one workflow to fan out across sheets automatically.

Header-driven mapping. Because rows are keyed by the header row, map spreadsheet columns to domain fields explicitly rather than relying on exact header names. Normalize header keys (trim, lowercase, replace spaces) in a converter or custom reader subclass, and reject duplicate/blank headers early.

Web uploads and queues. Move the uploaded file to a temp path, validate size/MIME/extension, then dispatch an import job. Keep PhpSpreadsheet loading out of the HTTP request for anything beyond tiny files. Treat the spreadsheet as untrusted input: enforce row/column/file-size limits before loading.

Styling and formatting extension. The adapter writes values; it generally does not preserve workbook styling. For production exports, subclass the writer, keep a reference to the underlying PhpOffice\PhpSpreadsheet\Spreadsheet/sheet, set column widths, number formats, freeze panes, and write the header yourself in writeItem()/finish(). This is also the place to add formula-injection protection.

Gotchas and Tips

  • Memory is the main constraint. PhpSpreadsheet loads the workbook into memory. XLSX import of tens of thousands of rows can exhaust PHP memory. Set realistic memory limits, cap uploads, process in chunks, and use CSV streaming for large flat files.
  • Dates and numbers arrive as spreadsheet types. Depending on setReadDataOnly and cell formatting, dates may appear as DateTime objects or Excel serial floats, and numbers may be strings. Convert explicitly; never trust spreadsheet typing.
  • Formula cells can be empty. PhpSpreadsheet returns cached calculated values. If a file was generated without cached values, expect null where you see a formula in Excel. Decide whether to pre-calculate, reject formula cells, or read formulas as strings.
  • Header/data row indexing is a common off-by-one bug. Header and data rows are row-number based; confirm whether your version expects zero-based indexes. Always inspect getHeader() and the first two records on a real file.
  • Blank rows and ragged rows happen. Rows may have missing keys or trailing empty cells. Validate required keys and default missing values in a converter rather than letting writers fail later.
  • count() on a countable reader may be expensive or force full workbook loading. Do not use it for progress bars on large imports; count source rows or emit progress from the writer instead.
  • Call finish(). In a workflow this is handled for you; when using a writer manually, the file is not written until the writer’s finish/save step runs. Check output directory permissions and avoid writing over an open source file.
  • Sheet-name limits apply. Excel sheet titles are limited to 31 characters and cannot contain \ / ? * [ ] :. Set explicit safe sheet names for exports.
  • CSV is deceptively hard. Encoding, delimiter, and quote detection can guess wrong. If the source is CSV, prefer portphp/csv or configure a PhpSpreadsheet CSV reader explicitly.
  • Low adoption means pin versions. With a small star count, treat the package as a thin adapter you control. Pin portphp/spreadsheet and phpoffice/phpspreadsheet, wrap it behind your own import/export service, and add integration tests with representative XLSX/XLS/ODS files.
  • Debugging recipe: dump the reader header, iterate three rows with var_dump, then test the same file through IOFactory::load() directly. If PhpSpreadsheet shows the same odd value, the issue is upstream, not PortPHP.
  • Protect exports from formula injection. Prefix user-controlled strings beginning with =, +, -, or @ before writing them to spreadsheet cells.
  • Extension points: subclass the reader to supply a preconfigured PhpSpreadsheet reader (for example read-data-only or explicit CSV settings), and subclass the writer to control sheet creation, headers, styles, and the final save path. These are safer than patching vendor code.
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.
novasa/laravel-language-center
opencage/geocode
adolfoholzer/parameters
livenetworks/ln-starter
shitoudev/permissionmanager
johannesclimacus/laravel-tui
hocvt/seotools
vitalybaev/sypexgeo
amphibee/themosis-ignition
gito-me/fast-excel
frankyso/laravel-ipaymu
geolize-studio/laravel-chile
boomerangui/chat
cgv/localized-resource
noith/settings
kda/laravel-package
kda/filament-dynamic-navigation
bypersoft/signbox
pairus/product-data
fayne/laravel-filament-cms-core