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

waad/filament-import-wizard

Queue-powered 4-step CSV and Excel import wizard for Filament with smart column mapping, relationship linking, Spatie Translatable and locale merge support, dynamic schema validation, upsert, and live progress tracking for 100K+ row imports.

View on GitHub
Deep Wiki
Context7

Getting Started

Install and publish what the wizard needs:

composer require waad/filament-import-wizard

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

If Filament styles break after installation:

php artisan filament:assets

Look first at these package surfaces:

  • Waad\FilamentImportWizard\Actions\ImportWizardAction — the main Filament table/header action.
  • config/filament-import-wizard.php — modal width, chunk size, CSV delimiter, queue connection, queue name.
  • The published migration — required for import tracking/progress/error state.
  • Your target Eloquent model — the wizard derives fields, validation, and relations from the model and schema.

The fastest first use case is a Filament resource list page with a simple CSV import:

use Waad\FilamentImportWizard\Actions\ImportWizardAction;

protected function getHeaderActions(): array
{
    return [
        ImportWizardAction::make()
            ->forModel(\App\Models\Product::class),
    ];
}

Then open the resource list, click the import action, upload a CSV, map name, sku, price, review, and run the import. For day-to-day work, treat the wizard as a replacement for hand-written CSV import screens, not as a data migration tool.

Before relying on it in production, verify:

  • Queue workers are running if you use background processing.
  • The model’s fillable/guarded behavior is understood.
  • Required fields and unique constraints match the import file.
  • The CSV headers are clean and consistently named.

Implementation Patterns

Add the action to a resource table

The normal pattern is to attach ImportWizardAction to a Filament resource page:

use Waad\FilamentImportWizard\Actions\ImportWizardAction;

protected function getHeaderActions(): array
{
    return [
        ImportWizardAction::make()
            ->forModel(\App\Models\Product::class)
            ->chunkSize(500)
            ->queueConnection('redis')
            ->queueName('imports'),
    ];
}

Use per-action queue settings when imports are expensive and should not compete with default jobs. Use the config defaults for consistency across the panel.

Prefer model-level import contracts

Instead of scattering validation logic across resources, define import-specific behavior on the model:

class Product extends Model
{
    public function getImportRules(): array
    {
        return [
            'sku' => ['required', 'string', 'max:50'],
            'price' => ['required', 'numeric', 'min:0'],
            'stock' => ['nullable', 'integer', 'min:0'],
        ];
    }

    public function getImportFieldLabel(string $field): string
    {
        return match ($field) {
            'sku' => 'SKU (Stock Keeping Unit)',
            'price_in_cents' => 'Price (cents)',
            default => Str::title(str_replace(['_', '.'], ' ', $field)),
        };
    }
}

This keeps import rules close to the model and makes the mapping step more understandable for non-technical users.

Use upsert for recurring imports

For supplier feeds, nightly syncs, or any file that may contain existing records, configure upsert explicitly:

ImportWizardAction::make()
    ->forModel(\App\Models\User::class)
    ->enableUpsert(true)
    ->upsertKeys(['email']);

Pick stable natural keys such as email, sku, or an external ID. Avoid using nullable or mutable columns as upsert keys.

Model relationship imports deliberately

For BelongsTo relations, map the CSV column to the relation field rather than the foreign key directly:

CSV Column: "Category Name" → Relation: category → Field: name

The wizard resolves numeric values against auto-incrementing IDs and string values against suitable string columns. Name your CSV columns clearly, and prefer a stable related field such as name or code. If relation resolution is ambiguous, provide an explicit related model field in the mapping step.

Use locale merge for translated content

For Spatie Translatable models or JSON columns, enable the merge translation toggle on the translatable field:

CSV Header Maps To
title_en title with locale en
title_ar title with locale ar

The stored value becomes:

{"en": "Hello", "ar": "مرحبا"}

This is useful for multilingual product/category imports. Keep locale suffixes consistent (_en, _ar) so auto-detection works.

Plan for standalone Livewire usage

Outside a Filament panel:

@livewire('filament-import-wizard', ['modelClass' => \App\Models\Product::class])

Use this only if you need the wizard in a non-Filament admin page. Otherwise, stay with the Filament action so you inherit panel styling, auth, and tenant scoping.

Handle tenancy at the model/job level

The package advertises tenant scoping across queue boundaries, but your application still owns tenant context:

  • Ensure imported models receive tenant_id/team_id from the mapping or model defaults.
  • Do not rely on the current request’s tenant being available inside queued jobs unless the package explicitly persists it.
  • Test a tenant-scoped import end-to-end before enabling it for customers.

Treat import files as user input

Build a small pre-flight process for operational imports:

  1. Validate file extension and encoding.
  2. Confirm required headers are present.
  3. Run a small sample (10–50 rows) through the wizard.
  4. Inspect the review step for validation errors.
  5. Run the full import with queue workers active.
  6. Download the error CSV if the import completes with errors.

For 100K+ row files, keep chunk_size moderate (500–1000) and ensure the queue worker timeout and memory limits are sufficient.

Gotchas and Tips

Queue workers are not optional for large files

If the queue connection is sync, imports run immediately and can timeout or exhaust memory. For real imports, configure a real queue connection and run workers:

php artisan queue:work redis --queue=imports

Keep the queue name in config and in the worker command aligned.

Publishing the migration is required

The wizard tracks import state, progress, and errors. If the migration is not published and run, imports may fail or progress will not be available. Always run:

php artisan vendor:publish --tag="filament-import-wizard-migrations"
php artisan migrate

Check model guarded/fillable behavior

The package handles $guarded = [] by falling back to schema columns, but this can mask unintended mass assignment. For production models:

  • Prefer explicit $fillable.
  • Use getImportRules() to constrain values.
  • Verify that foreign keys and computed fields are not accidentally importable.

Relation lookup can resolve more than you expect

String values for an auto-incrementing relation may be matched against a suitable string column and existing records may be reused. Numeric values are matched by ID. This is convenient, but it can also link to the wrong record if names are not unique.

For critical relations:

  • Use stable codes/SKUs rather than display names.
  • Enforce uniqueness on the matched field.
  • Review the mapping step carefully.

Validation is schema-derived unless you override it

Without getImportRules(), rules are inferred from database column types, nullability, and uniqueness. That is a good default, but it may miss domain rules such as:

  • Price must be positive.
  • Status must be in an allowed set.
  • Related records must exist.
  • A date must be in the future.

Add getImportRules() whenever business rules matter.

Upsert changes event behavior

Non-upsert imports save records individually as models, firing model events, observers, and timestamps. Upsert mode uses bulk upsert() and may not fire the same Eloquent lifecycle events per row.

If your application depends on observers for search indexing, notifications, or audit logs, decide whether upsert is appropriate or whether you need a post-import reconciliation job.

CSV parsing details matter

  • UTF-8 BOM is auto-detected, but malformed encodings can still break imports.
  • Delimiters can be configured globally, but mixed-delimiter files will fail.
  • Excel imports trim trailing empty columns, but embedded blank rows can still appear as empty records depending on mapping and validation.
  • Keep header names simple and consistent; auto-mapping relies on snake_case-style matching.

Auto-mapping is helpful, not authoritative

The mapping step auto-matches headers such as Title → title and CategoryId → category_id, but always review the mapping for:

  • Columns with spaces or punctuation.
  • Ambiguous headers such as name on both the model and a relation.
  • Translatable columns that should be split by locale.
  • Unmapped columns that the user expected to import.

Use the error download as an operational tool

Validation errors are shown per row and can be downloaded as CSV. Treat that file as the correction artifact:

  1. Fix rows in the source spreadsheet.
  2. Re-import only the corrected rows.
  3. Avoid re-importing already-successful rows unless upsert keys make it safe.

Watch unique constraints during review

The review step can catch many validation errors, but database-level unique constraints may still fail during processing. If imports complete with errors, inspect both the wizard’s error output and application logs.

Test with the actual production queue configuration

A common failure mode is a successful local import using sync and a broken production import using Redis/database queues. Test with:

  • The same queue connection.
  • The same worker timeout.
  • The same chunk size.
  • A file large enough to span multiple jobs.

Asset rebuild after install

If the modal renders incorrectly or styles are missing, run:

php artisan filament:assets

This is especially relevant after upgrading Filament or changing package assets.

Version maturity

The package is early-stage and has a small public footprint. Before standardizing on it:

  • Pin the version in composer.json.
  • Add an application-level test that runs a small import.
  • Monitor releases and changelogs.
  • Keep a fallback path for critical imports, such as a custom Artisan command or job.

Extension points to remember

Use these model methods and action methods before forking the package:

  • getImportRules() — custom validation.
  • getImportFieldLabel() — nicer mapping labels.
  • forModel() — explicit target model.
  • chunkSize() — tune for file size and worker memory.
  • enableUpsert() / upsertKeys() — idempotent imports.
  • queueConnection() / queueName() — isolate import workloads.
  • setModalWidth() — adjust UX for wide mapping tables.

If the package’s field derivation or relation resolution does not fit your domain, prefer model-level rules and clean CSV contracts over patching package internals.

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.
steadrun/laravel-monitor
labyrinthe/connect-laravel
elmogy/layan
drewdan/sentinel-client
dskripchenko/laravel-llm
fatryst/nuonuo
stevenay/myanfont
gmostafa/php-graphql-client
xeviant/paystack
snowsoft/faker
meditationsteps/blogetc
animalyoung/laravel-influxdb
onix-solutions/sms-telera
lonely-lights/prosetta
oxygen/data
oxygen/core
jengo/base
xiloryx/log-collector
a21ns1g4ts/filament-stripe
la-php/laravel-admin