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

Fast Excel Laravel Package

rap2hpoutre/fast-excel

Fast Excel import/export for Laravel, powered by Spout. Export Eloquent models, queries, or collections to xlsx, ods, or csv with a simple API, customize columns via callbacks, and handle large files efficiently. Benchmark-tested for speed.

View on GitHub
Deep Wiki
Context7

Technical Evaluation

Architecture fit

FastExcel is a deliberately thin adapter: a single FastExcel class wrapping OpenSpout's reader/writer, plus optional facade and global helper. It is stateless, has no service provider requirements for the core API (the facade alias is optional), publishes no config, and requires no migrations. That makes it a low-coupling choice for a Laravel application — it slots in behind a controller, an artisan command, or a queued job without touching application structure.

The one architectural caveat a TPM should enforce: do not let Rap2hpoutre\FastExcel\FastExcel leak across the codebase. Because it is a thin wrapper over OpenSpout, upstream changes (Box/Spout → OpenSpout, Style namespace moves) surface directly in calling code. Budget a small internal port — an ImportService/ExportService interface with a FastExcel-backed implementation — so swapping to Laravel Excel or a direct OpenSpout integration later is a single-class change rather than a codebase-wide refactor.

Integration feasibility

Install is one composer line; a working export is roughly five lines of code. The README is unusually complete for a package this size, covering multi-sheet, CSV delimiters/encodings, per-column types, styling, row limits, and the memory characteristics of every code path. Realistic effort:

  • Spike (one export + one import against a real file): 1–2 developer days.
  • Production-grade vertical slice (validation, transaction handling, async execution, observability): 1–2 weeks including review.
  • Full replacement of an existing Laravel Excel footprint: 3–6 weeks depending on feature surface, not on this package's complexity.

The dominant effort is not integration — it is the validation, error-handling, and job orchestration that FastExcel intentionally does not provide.

Technical risk

The package's stated position is "simple tasks only," and the risk profile follows directly from that. The failure modes are developer-induced rather than package bugs, but they are severe and easy to trip:

Risk Severity Notes
Materialized collections defeat streaming High User::all() before export() loads everything; README documents >512 MB failure on 1M rows vs ~4 MB with cursor(). A code review omission converts a working feature into an outage.
No validation layer High Row callbacks receive raw header-keyed arrays. Type coercion, missing columns, and null headers are the caller's problem. Silent data corruption is plausible.
Header/column coupling Medium Import logic keys off exact header strings. A renamed column upstream yields missing-key warnings or nulls, not a clean error.
Date and numeric coercion Medium Excel serial dates and leading-zero loss (phone numbers, IDs) require explicit stringValues()/setColumnFormat(). Easy to miss until production data is wrong.
Untrusted file upload Medium Parsing user-supplied XLSX/ODS introduces zip-bomb and malformed-XML exposure inherited from the underlying reader. Needs upload size caps and isolated processing.
Partial-write imports Medium Row-by-row User::create() inside the callback is not atomic by default. A mid-file failure leaves a half-imported table unless the caller wraps a transaction or uses staging.
Bus factor / governance Medium Small contributor base, no vendor SLA. History shows steady maintenance (v5.9 → v5.14 with CI, security policy, issue templates) but continuity is not contractual.
Feature ceiling Low–Medium No formula evaluation, charts, queued-import machinery, or rich validation. Requirements creep past the wrapper means either raw OpenSpout or Laravel Excel.

Key questions

  1. What are the realistic upper bounds — max rows and file size for imports, max rows for exports, and peak concurrency?
  2. Are imports synchronous (request-bound) or async (queued)? This single answer drives most of the architecture and the risk posture.
  3. What is the current package, and which of its features are actually used? Any formula, chart, styled-template, or queued-import dependency rules FastExcel out for that flow.
  4. Is incoming data trusted (internal finance export) or untrusted (customer upload)? Untrusted raises the priority of size caps, sandboxing, and type forcing.
  5. Who owns row-level validation and error reporting — application code, or do we need a UI showing failed rows?
  6. What is the PHP/Laravel version matrix, and does the OpenSpout version constraint conflict with anything already installed?
  7. Is there a policy requirement (SBOM, license scan, security advisory monitoring) that a solo-maintainer dependency must satisfy?

Integration Approach

Stack fit

FastExcel assumes Laravel plus OpenSpout, which pins PHP 8.1+ for current versions. It composes naturally with the rest of a standard Laravel stack: Eloquent cursor()/lazy() for streaming sources, Storage for landing exported files, queues plus Horizon for long-running imports, LazyCollection::chunk() for batched inserts, and Laravel's validator applied inside the row callback. It is format-compatible where it matters (xlsx, ods, csv, with configurable delimiter, enclosure, and encoding such as GBK) and returns the absolute written path from export(), which makes it straightforward to hand off to storage or attach to a notification.

Migration path

Run this as a feature-by-feature replacement, not a big-bang swap:

  1. Inventory every existing import/export call site and tag it by features used (sheets, styling, formulas, chunk reading, queued import, validation hooks).
  2. Classify each as fits FastExcel, fits with work, or stays on the incumbent. Expect a minority in the third bucket; do not force them.
  3. Pilot one export and one import that are representative but low-blast-radius — an internal admin report export and a small reference-data import.
  4. Introduce the adapter so the pilot's call sites depend on our interface, not the package class.
  5. Retire the incumbent only when the remaining call sites are the ones we have consciously excluded, and document why.

Compatibility

Two things to verify before committing:

  • Namespace lineage. FastExcel's public surface is stable, but styling and reader/writer options are typed against OpenSpout\... classes. Any existing code written against the older Box\Spout\... namespace will not port cleanly. Confirm the installed OpenSpout major and that Style, Color, and writer options used in examples resolve.
  • Version pinning. With roughly monthly releases adding methods (sheet-by-name, hideColumnsPrefixedWith, withSheetContext, writer configuration), pin to a caret-minor and update deliberately. Additions have been additive so far, but a thin wrapper means an upstream OpenSpout major is a coordinated upgrade, not a passive one.

Sequencing

Phase Scope Exit criteria
0 Spike: one export from a cursor(), one import via importLazy()->chunk() Both complete within memory budget on a production-shaped file
1 Export pilot behind the adapter, wired to storage (local/S3) and download response Feature parity with incumbent for that report; peak memory measured
2 Import pilot with per-row validation, staging table or transaction, and a failed-rows report Bad rows rejected with actionable errors; no partial writes
3 Async execution: queued job, progress state, retry and dead-letter handling Imports survive worker restart; operator can see status
4 Multi-sheet, CSV encoding, per-column type forcing as required by remaining call sites Remaining in-scope call sites migrated
5 Rollout, incumbent decommissioning, runbook and review checklist published No production call sites on incumbent

Phase 3 is the one most likely to be underestimated. It is entirely application engineering, and it is the difference between a demo and an operable feature.

Operational Impact

Maintenance

Low and predictable. No config file, no database objects, no scheduled tasks, no long-lived state. The recurring cost is dependency hygiene: monitor OpenSpout majors, run composer audit on the normal cadence, and watch this package's releases (the 5.9–5.14 window shows active maintenance, CI coverage, a security policy, and issue/PR templates — all positive signals). Budget a fraction of an engineer per quarter, plus a coordinated upgrade whenever OpenSpout majors.

Support

Support burden is almost entirely about developer misuse rather than package defects. The two failure patterns to institutionalize against are: passing a materialized collection to export() instead of a cursor or generator; and returning a value from an import callback when only the side effect is wanted (which accumulates every row in memory and reintroduces the exhaustion you were avoiding). Both are documented, both are invisible in code review unless someone is looking. Publish a short review checklist: cursor or generator on export; return null or importLazy on import; explicit stringValues/setColumnFormat for identity-like fields; headers treated as a contract.

Scaling

Streaming is genuine — the README's benchmarks (10k × 20: 4 MB generator vs 218 MB Laravel Excel; 1M rows under 512 MB) hold because rows never accumulate. Scaling therefore stops being a memory problem and becomes a throughput and scheduling problem:

  • Bound concurrency by queue workers, not by PHP-FPM processes; large exports on the request path will hit max_execution_time long before memory.
  • Chunk writes (->chunk(1000) + insert()) rather than per-row create() — the difference is order-of-magnitude on import wall time.
  • Size temp storage and set retention/cleanup on generated files; a popular export endpoint silently fills the disk.
  • Enforce upload size caps and a maximum row count (limitRows() is available for defensive truncation) before parsing begins.

Failure modes

Mode Symptom Mitigation
Memory exhaustion 500 with "Allowed memory size exhausted" on large files Streaming sources only; memory ceiling in the job; alert on the error string
Request timeout Truncated file or gateway timeout on export download Move to queued job + stored file + notification/download link
Partial import Some rows present, no error surfaced Transaction or staging table with promote step; idempotency key per import
Header drift Nulls or Undefined array key in row callbacks Validate the header row up front; fail fast with a named missing column
Type coercion Leading zeros stripped, IDs changed, dates shifted stringValues() + setColumnFormat(); golden-file tests asserting cell types
Encoding corruption Mojibake on CSV with non-UTF-8 source configureCsv(delimiter, enclosure, encoding); assert round-trip in tests
Untrusted file abuse Worker hangs or memory spike on a crafted XLSX/ODS Size cap, format allowlist, parse in an isolated queue with limits
Temp/disk leak Disk fills over weeks Cleanup job for export artifacts; monitor volume

Ramp-up

Genuinely fast: a mid-level Laravel developer is productive in half a day on the README alone, and the API is small enough to hold in working memory. The real ramp-up is organizational, not individual — getting the team to internalize when FastExcel is appropriate. Position it explicitly as the tool for "simple, high-volume, memory-sensitive tabular I/O," and keep a documented list of requirements that force escalation to Laravel Excel or raw OpenSpout (formulas, charts, styled templates, complex cell-level validation, queued-import UI). Without that boundary written down, the first complex export request will pull the package past its design envelope and the resulting workaround will be more expensive than the feature.

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.
muka/shape-reader
tdt/json
refinery29/php-cs-fixer-config
hotmeteor/eco-env
mvaliolahi/hydrate
wearede/tbcpay-php
parsnick/steak
thibaud-dauce/mattermost-php
multilinguals/apollo-client
antaresbr/collection
kr0lik/phpstan-rules
loco/loco
starfederation/datastar-php
yupmin/modern-phystrix
stephenjude/extended-artisan-commands
lumphp/laravel-macroable
lumphp/laravel-collections
lumphp/laravel-contracts
sermepa/sermepa
arkecosystem/fortify