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

konnco/filament-import

Konnco Filament Import adds CSV and Excel import actions to Filament resources. Define fields with validation and uniqueness rules, map spreadsheet columns to model attributes, and import rows into Eloquent records from a simple, configurable action.

View on GitHub
Deep Wiki
Context7

Getting Started

Install

composer require konnco/filament-import

There is no panel plugin to register — the package ships Filament actions, so it plugs directly into an existing Resource. Publish config/assets only if you need to change the upload disk or sample-file location:

php artisan vendor:publish --tag="filament-import-config"

Pin the version to your Filament major. As of 3.0.0 the package has jumped two majors from the old 1.6.1 line, and the majors track Filament/PHP compatibility rather than package features:

  • 1.x — legacy Filament 2 era
  • 2.x — Filament 3 era
  • 3.0.0 — current line, adds Laravel 11 support and fixes local-disk uploads

Install with an explicit constraint ("konnco/filament-import": "^3.0") rather than floating, and confirm your Filament major before upgrading — see Gotchas.

First use case: import users from CSV on a Resource list page

use Konnco\FilamentImport\Actions\ImportAction;
use Konnco\FilamentImport\Actions\ImportField;

// In your ListUsers page (or the Resource's table header actions)
protected function getHeaderActions(): array
{
    return [
        ImportAction::make()
            ->fields([
                ImportField::make('name')->required(),
                ImportField::make('email')->required()->unique(),
                ImportField::make('password')
                    ->mutateBeforeCreate(fn ($value) => Hash::make($value)),
            ])
            ->handleRecordCreation(function (array $data) {
                return User::create($data);
            }),
    ];
}

The user clicks Import, uploads a CSV/XLSX, maps the uploaded columns onto your fields, reviews a preview/validation step, and confirms. Rows are then fed to handleRecordCreation.

Where to look first

  1. The README's example resource action — copy it verbatim into a throwaway Resource and confirm the flow works before customizing.
  2. ImportAction (modal, mapping, and creation pipeline) and ImportField (per-column rules and mutation hooks) — these two classes are the whole public surface.
  3. The Importer abstraction (if your version supports a dedicated importer class) — reach for it once the same field set is used in more than one place.
  4. The published config's disk key — since 3.0.0 local (non-S3) disks work again; verify which disk your uploads land on before debugging anything else.

Implementation Patterns

Inline fields vs. a dedicated importer class. Start inline (->fields([...])->handleRecordCreation(...)) for one-off imports. As soon as a second Resource or a second file format needs the same columns, move the field definitions and creation logic into a reusable importer class / service and point the action at it. This also makes the logic unit-testable without touching Filament's UI layer.

Mapping human-readable headers. Don't name fields after database columns; give them labels (ImportField::make('phone')->label('Mobile Number')). The mapping step becomes self-explanatory, and you can later rename the column without breaking saved imports.

Resolve foreign keys during mutation. Users will never have your category_id, they'll have a category name. Do the lookup in the field mutation and fail loudly if it can't be resolved:

ImportField::make('category_id')
    ->label('Category')
    ->mutateBeforeCreate(fn ($value) => Category::firstOrCreate(['name' => trim($value)])->id),

For multi-pass relationships (orders + order lines), run two separate imports in sequence rather than trying to nest.

Upsert / idempotent re-imports. The common real-world need is "re-upload the corrected spreadsheet." Mark the natural key ->unique() and handle the update branch explicitly (via handleRecordUpdate, or by doing updateOrCreate inside handleRecordCreation). Without this, the second upload creates duplicates and the user blames the feature.

Tenant and ownership scoping. Multi-tenant panels: never trust a tenant_id column from the spreadsheet. Inject it in the mutation from Filament::getTenant() (or auth()->id() for an owner column) so imports can't cross tenant boundaries.

Sanitize data types in mutations, not in the database. Excel gives you untyped strings: trim whitespace, strip currency symbols, parse dates with Carbon::parse, uppercase country codes, normalise phone numbers. Every one of these belongs in mutateBeforeCreate/a shared normalizance, otherwise the data quality problem shows up as a support ticket.

Guard the action. Treat import as a privileged operation: ->visible(fn () => auth()->user()->can('import users')) plus a policy check on the model. Import is a bulk-write bypass of the edit form and deserves its own permission.

Large files. The action is request-bound and synchronous. Set a hard row cap in the modal description, validate file size via the uploaded file rules, and for anything above a few thousand rows route users to a queued Laravel Excel job instead. Don't let the UI promise more than PHP's max_execution_time can deliver.

Testing. Extract the creation closure to a class and test it with plain arrays — you get 90% of the coverage without driving a Livewire file upload.

Gotchas and Tips

  • The 1.x → 3.x jump is a compatibility boundary, not a feature bump. The 3.0.0 release bundles the Filament-major alignment plus Laravel 11 support; release notes are sparse, so treat any upgrade as "read the changelog and smoke-test imports." Pin in composer.json and re-verify after every Filament or Laravel upgrade — this is not a package to float on *.
  • Local disks were broken on the pre-3.0 beta track. 3.0.0 ships the fix ("import with local disks not working after last beta release"). If you're developing against the beta or copied beta-era config, set disk explicitly in the published config to your local/public disk and re-test; silently failing uploads/reads are the symptom.
  • Laravel 11 support is new here. Older majors were not cleared for Laravel 11 — if you're on an L11 app you're effectively forced onto ^3.0, which in turn implies a specific Filament major. Check the full constraint matrix before planning the upgrade.
  • Duplicate keys inside one file slip past ->unique(). The uniqueness check usually runs per row against the database, so two identical emails in the same spreadsheet can both pass the preview. Dedupe the parsed collection yourself, or use updateOrCreate so the second row simply overwrites the first.
  • Mass inserts bypass what you expect. If your version offers a fast/bulk insert mode, model events, observers, mutators, and created_at defaults may not fire. Check whether your business logic lives in observers before enabling it.
  • CSV quirks. UTF-8 BOMs corrupt the first header cell, semicolon-delimited exports (common from European Excel locales) won't parse, and trailing empty rows create blank records. Validate that required fields are non-empty and trim header names before matching.
  • Excel eats leading zeros. Phone numbers, postal codes, SKUs, and NIKs become 8123456 or 1.23E+15. Store these as string columns, format the template column as text, and cast explicitly in the mutation.
  • Dates arrive as serial numbers. Excel dates can surface as integers (e.g. 44927). Detect numeric date fields and convert inside the mutation rather than expecting a date string.
  • Partial imports are the default failure mode. There's no wrapping transaction across all rows — a mid-file exception leaves earlier rows committed. Either wrap the creation handler in DB::transaction for small files, or accept partial imports and surface a clear "N imported, M failed" summary to the user.
  • File size limits are PHP's, not the package's. upload_max_filesize and post_max_size failures manifest as a silent "no file" validation error. Compare the two values in php.ini before debugging the action. With the new local-disk fix, a missing storage:link is the next thing to check.
  • Always ship a sample template. Pointing users at a downloadable template removes the entire class of "my columns don't map" complaints and is the highest-ROI five minutes of configuration.
  • Debugging inside the pipeline: dd() in a mutation will kill the Livewire request — write to Log::info() with the row payload instead. Reproduce bugs by feeding the raw array into your creation handler in a tinker session or test, not by re-uploading through the browser.
  • Extension points to remember: ImportField can be subclassed to package up a common pattern (e.g. a TenantScopedField), the action supports the usual Filament modifiers (modalHeading, modalDescription, icon, visible, authorize), and the language files are publishable if you need translated labels for non-English staff.
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