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

Nova Import Laravel Package

anaseqal/nova-import

A Laravel Nova package for importing CSV and Excel files into Nova resources. It adds an import action with column mapping, validation, and customizable import logic, making bulk data entry from spreadsheets easier directly inside your Nova admin panel.

View on GitHub
Deep Wiki
Context7

Getting Started

Check the version wall before you invest an hour. This package ships its last release in August 2020 and is built for Nova 3 / Laravel 6–8 / PHP 7.2–7.4, sitting on maatwebsite/excel ^3.1. On Nova 4/5 the compiled Vue assets won't mount and the toolchain mismatch will surface only after nova:publish. Treat that as a fork-and-reimplement signal, not a bug to debug.

On a compatible Nova 3 install the setup is quick:

composer require anaseqal/nova-import
php artisan vendor:publish --provider="Anaseqal\NovaImport\ToolServiceProvider"
php artisan nova:publish

Register the tool in app/Providers/NovaServiceProvider.php:

public function tools()
{
    return [
        new \Anaseqal\NovaImport\NovaImport(),
    ];
}

Read the source in this order before writing any glue code:

  1. src/ — the tool class plus the upload/import controller. This is the documentation: it shows the exact file → parse → map → validate → Model::create() flow.
  2. resources/js/ — the Vue modal that posts the column mapping. Read it to learn the payload contract, because that's what you'll extend or intercept.
  3. composer.json — the exact pins it forces on Nova and maatwebsite/excel. Run composer why maatwebsite/excel before installing if you already have export/reporting code.
  4. config/ — if a config file is published, that's where disk, queue, and validation defaults live.

First task: import one boring reference table. Pick a flat, single-table model (countries, carrier list, product categories) with 40–100 rows of scalar columns, import into a scratch DB, and diff the result against the source row by row. This surfaces header matching, type casting, date handling, and silent row-skips in under an hour. Do not start with a table that matters.

Implementation Patterns

Gate the tool, don't just rely on Nova auth. The upload endpoint is authenticated but not otherwise authorized, so canSee() is your primary lever:

(new NovaImport())->canSee(fn ($request) => $request->user()->hasRole('ops-admin'))

Pair it with a policy on the target model. If ops asks "who can import what?", the answer must live in code, not in a Nova permission you'll forget to audit.

Curate the mapping field list explicitly. Don't let the mapping UI infer from $model->getFillable() or the schema. Enumerate accepted columns in a config array, keep it in git, and version it alongside validation rules. When someone adds a model column, the importer must not silently start accepting it — that's how unvalidated data lands in production.

// config/imports.php
'products' => [
    'fields'  => ['sku', 'name', 'price', 'category_id'],
    'rules'   => [
        'sku'         => 'required|string|max:64',
        'name'        => 'required|string|max:255',
        'price'       => 'required|numeric|min:0',
        'category_id' => 'required|exists:categories,id',
    ],
],

Tag every import with a batch ID. The package's per-row validation flashes errors to the browser and discards them; "why didn't row 47 import?" becomes unanswerable. Wrap the import and persist a batch reference on the target table (or a dedicated import_batches table):

DB::transaction(function () use ($rows, $batchId, $rules) {
    foreach ($rows as $row) {
        $validated = Validator::make($row, $rules)->validate();
        Product::create([...$validated, 'import_batch' => $batchId]);
    }
});

That single column buys you rollback (DELETE WHERE import_batch = ?), an audit trail, and a way to reason about half-finished runs. On a package with no maintainer, this is the highest-leverage addition you can make.

Choose insert-only vs. upsert up front. The package has no dedupe semantics — re-running a file creates duplicates or trips unique constraints. Decide now:

  • Insert-only — make the unique DB constraint the guardrail, and surface the violation back to the user.
  • Idempotent — pre-load a natural-key map (SKU → id) and do updateOrCreate in your own post-parse callback.

Do transformations in a post-validation callback, not model mutators. Slugs, phone normalisation, "category by name → category_id", default fills for blank columns — put these in the import pipeline. A mutator fires on every write path and you'll spend a Friday debugging why an admin edit changed a slug unexpectedly.

Queue anything over a few hundred rows. The package advertises chunked processing and queue support, but on a five-year-old codebase verify against your driver before trusting it. If the built-in path is shaky, the reliable pattern is:

  1. Upload handler stores the file to a private disk.
  2. Dispatch your own job with the file path + mapping payload.
  3. Job reuses the package's parser, chunk-reads 500–1000 rows, wraps each chunk in a transaction, and writes progress to the batch record.
  4. Nova UI polls the batch row for status.

Test with checked-in fixtures. A 20-row CSV in tests/Fixtures/ plus a feature test asserting row count and two field values is cheap and will catch every Nova/Excel version bump. Add a second fixture of deliberately bad rows and assert the count of rejected rows.

Gotchas and Tips

  • maatwebsite/excel is the conflict magnet. If you already export or report via Excel, expect a negotiation you may lose. Check composer why maatwebsite/excel before and after.
  • The Nova 4/5 wall is front-loaded. Prove asset compilation works in the first hour of any spike — not after a day of wiring.
  • Header matching is literal. A UTF-8 BOM, a trailing space, a capital letter, or a semicolon delimiter produces zero mapped columns silently. Validate headers against your expected list and refuse the file with a clear message; strip BOMs on read.
  • Excel mangles identifiers. Leading-zero SKUs and postcodes become numbers; long numeric IDs become scientific notation. Force those columns to strings on both sides and tell users to upload CSV.
  • Excel serial dates. A date column from .xlsx may arrive as 44197 instead of 2021-01-01. Handle in the transformation step or normalise to CSV as an input requirement.
  • Mass assignment silently drops columns. If a mapped column isn't in $fillable, the row imports "successfully" minus that data. Run your first import against $guarded = [] in a scratch environment to prove the pipeline, then tighten.
  • The synchronous ceiling is lower than you think. max_execution_time, memory_limit, and post_max_size stop you in the low thousands, well before the DB notices. Enforce a row-count cap server-side in the handler — a Vue-side check is bypassable.
  • Enforce upload safety on the server. MIME, extension, file size, row cap. An admin upload endpoint is still an upload endpoint.
  • Partial writes leave no trace. Without a transaction and a batch tag, a mid-file timeout gives you an unknown table state and no way to identify or reverse it. This is the failure mode that turns a demo into an incident.
  • Extension points to know: custom field/dropdown types for the mapping UI, post-import callbacks, and pointing the tool at a resource other than the one being viewed. The mapping payload is the contract — read resources/js/ before extending it.
  • Vendor and pin. With no release in five years and zero dependents, fork into a private repo, pin the commit, own the fixture tests, and name an owner. Budget a couple of engineer-days per Nova major upgrade. Writing the README the package never had is the cheapest risk reduction available.
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
shitoudev/permissionmanager