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 Import Laravel Package

coolsam/excel-import

A Laravel/PHP package for importing Excel files into your application. It offers a simple API for reading spreadsheets, mapping rows to models or arrays, and handling common import tasks with minimal configuration—useful for batch data loads and admin imports.

View on GitHub
Deep Wiki
Context7

Getting Started

Confirm what actually ships before you plan a day around it. The repo name is filament-excel-import, the description promises imports and exports, but the only changelog line available (v1.2.0) is "Support for Laravel 12." Before you scope anything, tree src/, grep -ri export src/, and read the plugin registration class. If export surfaces don't exist, an "export prices to CSV" ticket is a new feature built on maatwebsite/excel, not a config toggle.

Prerequisites that actually matter day-to-day:

  • A working Filament panel. Without Filament, skip this package and use maatwebsite/excel directly — the entire value here is the panel integration.
  • PHP 8.2+ on Laravel 12 (framework floor, not the package's).
  • A queue driver and a filesystem disk you control, with a cleanup plan for temp uploads.
  • maatwebsite/excel + phpoffice/phpspreadsheet arriving transitively. PhpSpreadsheet's memory profile is the real constraint on your import sizes.

Install and register:

composer require coolsam/excel-import:^1.2   # ^1.2 required for Laravel 12
php artisan vendor:publish --tag="excel-import-config"   # verify tag name in src/ first
// AdminPanelProvider::panel()
return $panel
    // ...
    ->plugin(\Coolsam\ExcelImport\ExcelImportPlugin::make()); // confirm FQCN in src/

Read in this order, and don't skip step 2:

  1. README.md — assume it lags the code until proven otherwise.
  2. src/*Plugin.php — what actually registers on the panel (actions, pages, widgets).
  3. The importer contract/base class — this is your real API surface, and the thing your day-to-day code touches.
  4. The published config — chunk size, disk, queue connection, header row index.
  5. composer.json constraints on filament/filament and maatwebsite/excel — more predictive of breakage than any changelog.

First use case: one boring internal import on an existing resource. An operator-uploaded "Price list" or "Customers" file attached as a header action. Not customer-facing, not compliance-adjacent. Success criteria: action appears → upload → headers mapped → rows validated → queued → completion notification with counts. Timebox it to one day. If it fights you past that, you've spent a day and still have maatwebsite/excel.

The mental model to adopt immediately: this package is a thin UI adapter. Your leverage lives in the importer class you write, not in the plugin. That class is portable — it can run from a Filament action, an artisan command, or a queued job, and it survives if you drop the package.

Implementation Patterns

One seam, no vendor leakage. Never let Coolsam\ExcelImport\* types reach domain code:

// app/Contracts/SpreadsheetImport.php
interface SpreadsheetImport
{
    public function queue(string $path, string $importerClass, array $options = []): ImportHandle;
}

Implement once with the package. Dropping to maatwebsite/excel, vendor-patching, or rolling your own then costs one file instead of every call site. With zero stars and zero dependents, plan as if you're the first production user.

One importer class per model, not per screen. Header mapping, casting, and validation live there; the Filament action stays a few lines.

class CustomerImporter implements /* package importer contract */
{
    public function collection(Collection $rows)
    {
        foreach ($rows as $row) {
            Customer::updateOrCreate(
                ['external_ref' => $row['external_ref']],   // natural key = idempotency
                ['name' => $row['name'], 'email' => $row['email']],
            );
        }
    }
}

updateOrCreate on a natural key makes retries after partial failure safe. Do this on every re-runnable import — which is most of them.

The reuse pattern that pays off daily: the same importer class invoked from three places — the Filament action (operators), an artisan command (backfills, migrations), and a queued job (large files). Write it once with no Filament or Livewire coupling and you get backfill tooling for free. This is the single highest-leverage habit with this package.

Validate twice, at two different layers.

  • Upload-level (Filament form): mime, extension, max size, required header row present. Stops bad files before they hit the queue.
  • Row-level (importer): types, required fields, foreign key existence, date parseability. Produces the error messages operators can act on.

Verify queued + chunked is the actual default path. A package can expose both and still read the whole sheet synchronously. Test with a deliberately oversized fixture (25k rows, memory_limit=256M) and watch RSS. If memory climbs linearly, you're on the wrong code path — tune the config, not the worker.

Notify on completion and on failure, with an artifact. Queued imports are invisible. A Filament notification with row counts plus a downloadable failed-rows CSV turns "the import is broken" into "3 rows failed, here's which."

// failure path: collect ['row' => 47, 'raw' => [...], 'error' => '...'], export to disk

Testing pattern — four fixtures catch most regressions:

Storage::fake('local');
Queue::fake();

Livewire::test(ImportCustomers::class)
    ->set('file', UploadedFile::fake()->create('customers.xlsx'))
    ->call('import');

Queue::assertPushed(ImportCustomersJob::class);

Keep 3–5 row fixtures for: happy path, malformed header, duplicate natural key, validation failure.

Sync in dev, queue in prod. QUEUE_CONNECTION=sync locally makes importer failures debuggable with a normal stack trace instead of dd() inside a worker.

Gotchas and Tips

Assume nothing about transactions or idempotency. Chunked/queued imports routinely produce partial writes on failure and duplicates on retry, and the changelog says nothing. Wrap the importer body in DB::transaction() where volume permits; use natural-key upserts where it doesn't; persist the original file path so any failed run is reproducible.

Header normalization is a mandatory pre-step, not a nicety. Normalize before your importer sees a row:

  • UTF-8 BOM on the first CSV header cell — ltrim($h, "\xEF\xBB\xBF").
  • Trailing whitespace and inconsistent casing.
  • Blank header cells.
  • Localized dates (d/m/Y vs m/d/Y) — parse explicitly, never let PhpSpreadsheet guess.
  • Excel silently converting numeric IDs and phone numbers to scientific notation, or dropping leading zeros.

Build one canonical header map per importer and unit-test it.

Filament major bumps are the breakage axis, not Laravel. v1.2.0 proves the maintainer tracks Laravel; it says nothing about Filament, whose action/schema APIs move across majors. Pin exact versions of coolsam/excel-import, filament/filament, and maatwebsite/excel, and put a constraint audit in CI. A routine composer update is the likeliest thing to break your imports.

Temp disk cleanup is your job. Disk plus queue equals orphaned uploads. Delete in the job's finally, or add a scheduled prune.

Debugging a stuck import — in order: failed_jobs (it's almost always an OOM), then the job's row counter, then whether the temp file still exists on the disk visible to the worker. Log the row number and the raw row array on every validation failure; without that you're guessing which line broke.

Memory knobs by effect: chunk size down → queue:work --memory cap → worker memory_limit up → and never run the import inside the web request. A 10MB XLSX is not a 10MB memory cost.

Security surface is yours. Reject formula injection on any export path (=, +, -, @ prefixes) and validate uploads by extension and MIME. There's no evidence of a security policy or disclosure process here — treat file handling as code you own.

Extension points to keep open: the wrapper is thin by design, so maatwebsite/excel is always reachable underneath. If the package stalls or a Filament upgrade breaks it, replacement cost is swapping the Filament action while keeping the importer classes and the engine — a day or two, not a quarter. Vendors-patch rather than waiting; single-maintainer, zero-dependents means no upstream will notice your bug.

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