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

Filament Import Laravel Package

romansulzhyk/filament-import

A Filament plugin for importing records from CSV and Excel files into Eloquent models, with configurable columns, validation, and chunked processing for reliable admin-side data imports.

View on GitHub
Deep Wiki
Context7

Getting Started

filament-import gives you a Filament action that reads a spreadsheet/CSV file, maps rows onto your models through small Importer classes, and reports per-row failures back in the UI. As of v1.1.1 the package is explicitly CSV-first: delimiter and encoding are detected automatically, .tsv is a first-class format, and imports run synchronously (no queue) — which is great for feedback, but means file size is your responsibility.

First steps:

  1. Install the package and publish/register the plugin in your panel provider (follow the README install block; it is the only setup most projects need).
  2. Put ExcelImportAction::make()->importer(ProductImporter::class) into a resource's header actions, or into a custom page.
  3. Generate/scaffold an importer class and define getColumns(). This is the file you will edit for 90% of your day-to-day work: it is where header aliases, casting, and validation live.
  4. Export a real spreadsheet from whatever tool your users actually use (Excel, Google Sheets, LibreOffice, an ERP dump), then import that file rather than a hand-made fixture. Encoding and delimiter quirks show up there, not in your clean test CSV.

Your first use case should be the smallest one: one model, three or four columns, a required name/sku, one numeric column. Confirm the happy path and the error rendering before adding relationships, beforeCreate hooks, or multi-sheet logic.

Implementation Patterns

Keep importers thin and declarative. Define columns with their header aliases and let the package do the row matching:

public function getColumns(): array
{
    return [
        ImportColumn::make('sku')
            ->requiredMapping()
            ->rules(['required', 'string', 'max:64']),
        ImportColumn::make('name')->requiredMapping(),
        ImportColumn::make('price')->castStateUsing(fn ($state) => (float) str_replace(',', '.', $state)),
        ImportColumn::make('category.name')->relationship(), // resolve/create via relationship
    ];
}

Anything that is not a straight column cast belongs in resolveRecord() or a beforeSave() callback, not in the column definition — column callbacks re-run per row and are easy to make non-idempotent.

Header aliasing is your compatibility layer. Users will send EAN, EAN Code, ean_code, and EAN-Code for the same field, across locales. Prefer declaring aliases on ImportColumn over normalising headers in a preprocessing step; the aliasing lives next to the column and shows up in the UI's column mapper, which is what actually saves support time.

Uploads from user-facing screens: extend, don't replace, file types. Since v1.1.1:

ExcelImportAction::make()
    ->importer(ProductImporter::class)
    ->acceptedFileTypes([
        ...ExcelImportAction::ACCEPTED_FILE_TYPES,
        'application/octet-stream', // some ERP/browser combos label CSV this way
    ]);

acceptedFileTypes() replaces the defaults, so always spread the constant unless you deliberately want to drop a format. If your users upload from a system that serves CSVs with application/vnd.ms-excel, text/plain, or no type at all, add those rather than telling users to rename files.

Treat acceptance as UX, not security. The reader still decides format from file contents and extension, so an accepted MIME type never makes a binary file importable — and conversely, a rejected type is a client-side guard only. Validate the parsed data (rules on columns, unique constraints at the DB level) as if the file came from the internet, because it did.

Synchronous-by-design workflow. No queue means the user sees validation errors immediately, which is the right default for admin panels. Do not "fix" it by chunking the upload UI; instead:

  • Cap file size at the validation layer (->maxSize() / rules) to something your PHP max_execution_time and memory_limit can finish. A few thousand rows is usually fine; tens of thousands is a queue job you should write yourself by invoking the importer programmatically.
  • For big or nightly feeds, bypass the action entirely and call the importer from a command/job, keeping the same Importer class so column mapping stays single-sourced.

Testing. Test the importer, not the Livewire action. Construct the importer with a row array (or parse a fixture file) and assert records/errors; keep fixture files in tests/Fixtures with names that describe the quirk they encode: google-sheets-utf8-bom.csv, excel-de-semicolon.csv, erp-tabbed.tsv, no-extension-text-file.

Gotchas and Tips

  • acceptedFileTypes() is a whole-list replacement. This is the most likely source of a "worked before, now the upload is rejected" regression. Spread ExcelImportAction::ACCEPTED_FILE_TYPES and append.
  • New in v1.1.1: .tsv is tab-separated CSV. Files without .csv/.tsv/.txt are read as CSV only when the first kilobyte looks like text. Practical consequence: a .dat/.xls-mislabelled file with a long preamble, leading blank lines, or binary junk in the first KB may be rejected or mis-sniffed. If a customer's export fails to sniff, ask for the file — the cheap fix is renaming to .csv/.txt, the proper fix is upstream.
  • Sniffing is a heuristic; encoding detection is too. Windows-1252 exports ("Müller", "€") and Excel's BOM-prefixed UTF-8 both appear in the wild. If imported strings look mangled, verify the bytes (file -i, or hexdump -C | head) before changing importer code — the bug is nearly always the source file.
  • Delimiter detection and decimal commas. European CSVs use ; as delimiter and , as decimal separator. Detection handles the delimiter, not the number format: a price column arriving as 1.234,56 will cast to 1.234 silently. Cast explicitly and add a validation rule with a helpful message on the column.
  • Header rows are real data-shaped. Blank spacer rows above the header, merged title cells, or a second footer row will produce phantom columns and failed mappings. Check what the column mapper shows on the upload screen; if it looks wrong, the file has preamble — tell users to export from the first row.
  • Failures are per-row, not per-file. A row failing requiredMapping() is skipped and reported; the rest still import. That is usually desirable, but it makes partial imports easy to miss — always surface the failure summary to users, and make imports idempotent (updateOrCreate on a natural key) so a corrected re-upload does not duplicate rows.
  • Relationship columns create records. ->relationship() with resolve/create can silently add new categories/brands. Decide deliberately between relationship() and a manual lookup that errors on unknown values; for catalogue imports, erroring is often the safer default.
  • Config quirks and extension points. Publish the package config before overriding anything (column defaults, chunk size if exposed), and keep local overrides documented — upgrades will otherwise surprise you. The Importer class itself is the extension point: subclass it for shared behaviour (trimming, tenant scoping, audit logging in beforeSave) rather than repeating castStateUsing closures in every importer.
  • Pin and read the changelog before upgrading. This is a young package with a fast-moving CSV story; v1.1.x changed file-type handling and accepted formats. Run your fixture suite after every bump — the fixtures above are exactly the regression net you need.
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.
socialdept/atp-orm
cboxdk/laravel-assistant-client
socialdept/atp-resolver
socialdept/tenant-domains
tautvydasr/blade-class-names
socialdept/color-scales
maherelgamil/controller
coleus/widgets
coleus/table
coleus/users
coleus/music
coleus/health
coleus/calendar
coleus/notes
coleus/support
coleus/settings
coleus/apps
seampact/laravel
emmyconceptng/laravel-ng-postcode
jjsquady/round-robin