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

Voyager Import Laravel Package

joy/voyager-import

Voyager Import brings flexible CSV and Excel importing to Laravel Voyager admin panels. Map uploaded columns to BREAD models, validate each row, and bulk-insert records through a clean, configurable interface built for everyday admin use.

View on GitHub
Deep Wiki
Context7

Getting Started

Minimal steps

  1. Confirm your stack first — VoyagerImport is a Voyager module, not a standalone package. Verify composer.json on the repo against your laravel/framework and tcg/voyager versions before committing; the last release (Feb 2024) targets the Voyager 1.5/1.6 era.
  2. composer require joy/voyager-import
  3. Publish assets with the provider or tag the package ships (open src/Providers/*ServiceProvider.php and read publishes() — you want the config file and any migrations):
    php artisan vendor:publish --provider="Joy\VoyagerImport\VoyagerImportServiceProvider"
    php artisan migrate
    
  4. Clear caches, then reload the Voyager admin. Look for a new entry in the admin menu and/or an Import action on each BREAD's browse page.
  5. Confirm permissions: log in as a non-admin role and check that the import action respects Voyager's permission system (it usually registers a permission key you must grant).

Where to look first

  • README.md → install + screenshot of the import flow.
  • src/ → the service provider (what loads routes/menu/views), routes/, Http/Controllers/, and any Imports/ or Services/ directory. That tells you whether it wraps maatwebsite/excel or rolls its own reader — this drives everything else.
  • config/ → file size limits, chunk size, allowed extensions, and column-mapping defaults.
  • The Voyager BREAD editor → whether an "Import" checkbox exists per field.

First use case

Start with the least risky table you own: a lookup/reference table (categories, tags, countries) with 2–3 columns and a unique slug. Do not start with users or anything with password hashing, polymorphic relations, or media columns. Export 3 existing rows from Voyager, re-upload the same file as your first import, and verify it round-trips (mapping, types, timestamps) before touching real data.

Implementation Patterns

Pattern 1 — Admin UI import (the 90% path)

The intended workflow is: define the BREAD in Voyager → browse to the list view → click Import → upload CSV/XLSX → map file columns to BREAD fields → submit. The package typically maps strictly by column header name to the BREAD field name, so your header row should mirror the BREAD keys exactly (title, slug, status, category_id). Prefer this path for one-off data loads by ops staff; keep a sample template file in storage/app/templates/ and hand it out so headers never drift.

Pattern 2 — Programmatic import (seeders, artisan, migrations of legacy data)

For anything you'll do more than twice, don't click. Wrap the package's importer class in your own artisan command so it's versioned and repeatable:

// app/Console/Commands/ImportProducts.php
public function handle()
{
    $rows = Excel::toCollection(null, $this->argument('file'))->first(); // or the package's reader
    foreach ($rows as $i => $row) {
        $data = $this->map($row->toArray());
        $v = Validator::make($data, Product::importRules());
        if ($v->fails()) { $this->failures[$i + 2] = $v->errors()->all(); continue; }
        Product::updateOrCreate(['sku' => $data['sku']], $data);
    }
}

Key habit: updateOrCreate keyed on a natural unique column, never insert, so re-running is idempotent. Chunk with ->chunk(500) and commit per chunk — a single transaction over a 50k-row file will hold locks and blow max_execution_time.

Pattern 3 — Row-level validation with a failure report

Collect failures rather than aborting on the first bad row. Return a CSV of {row_number, errors, raw_row} to the user. Import tooling lives or dies on this: operators will upload a spreadsheet with one bad date in row 8,431 and need to know exactly which row.

Pattern 4 — Queued imports for large files

Above ~2–5k rows, move the upload to a job. Store the uploaded file, create an ImportLog record (status, total, processed, failures), dispatch ProcessImport, and poll/echo progress. This sidesteps web timeout limits and lets you retry. The package's controller usually does the work synchronously — subclass or replace it via a route/container binding rather than editing vendor code.

Pattern 5 — Per-model importer classes

If the package exposes an importer contract, implement one per BREAD (ProductImporter, CategoryImporter) with rules(), mapRow(), beforeSave(), afterSave(). Centralizes the "how do we handle this column" knowledge instead of scattering casts across the UI config.

Integration tips

  • Keep BREAD fields and DB columns in sync; the importer writes through the model, so $casts, mutators, and booted() hooks do fire. Use them — a slug mutator means you can omit slugs from the CSV.
  • Import foreign keys as IDs, not names. If your operators only know names, add a resolveCategory() step in mapRow() before save.
  • For Voyager BREAD fields flagged required with no default, either exclude them from import or set defaults in beforeSave().
  • Gate the feature: only enable import for tables where a unique key exists. Uniqueness is what makes retries safe.
  • Add DB::transaction per chunk, not per file; log the chunk's byte offset/row range so a failed run resumes rather than restarts.

Gotchas and Tips

File & environment

  • upload_max_filesize / post_max_size / max_execution_time will bite before the package does. Check php -i | grep -E 'upload_max|post_max'; a 5MB default silently rejects a 12MB XLSX with a generic validation error.
  • UTF-8 BOM from Excel's "CSV UTF-8" export prepends an invisible \xEF\xBB\xBF to your first header, so title never matches. Strip it ($first = preg_replace('/^\xEF\xBB\xBF/', '', $first)) or save as plain CSV.
  • Excel mangles data on the way in: leading 0 in phone numbers, 16-digit IDs → scientific notation, dates in local locale. Format those columns as Text in the template, or cast explicitly on read. If the package uses maatwebsite/excel, date columns may arrive as Carbon instances or Excel serials depending on WithHeadingRow/format config.

Data integrity

  • Empty string ≠ NULL. Validate '' → null before save, or your nullable integer columns become 0 and your unique indexes collide on ''.
  • Foreign keys fail inside the chunk transaction and take the whole chunk down. Validate references in a pre-pass.
  • Slug/title collisions: Voyager's own BREAD may add a unique rule at the controller layer that the importer bypasses. Replicate the uniqueness rule in mapRow() (or rely on updateOrCreate keying).
  • Media, images, passwords, and polymorphic relations are almost never handled by generic import modules — expect to write custom handling or exclude those columns.

Debugging

  • Always run a 3-row file through first. Then a 300-row file. Then production size. Most import bugs are encoding or type bugs that only surface at scale (memory), or only surface immediately (BOM).
  • Log the raw row and the source row number (header offset +1) — the single most useful debugging affordance you can add.
  • If the import silently imports 0 rows, check header/field name mismatch and permission denial before anything else. Both fail quietly.
  • After publishing config, re-run php artisan config:clear; a stale compiled config is a classic "my settings didn't apply" trap.

Maintenance risk & extension points

  • 7 stars, 0 dependents, single maintainer. Treat it as a thin convenience layer, not infrastructure: keep your own app/Imports/* classes so you can drop the package without losing business logic, and pin the version ("joy/voyager-import": "~1.0") so a composer update can't break the admin UI.
  • The real extension points are: the service provider's bindings (swap the importer implementation), the config file (limits/allowed types), and the model layer. Prefer model-level hooks (saving, saved) over vendor patches — they survive upgrades and package removal.
  • Watch Voyager compatibility independently: Voyager 1.6.x is the practical ceiling for recent Laravel versions. If you're on Laravel 11, budget time to fork or port the controller rather than assume it works.
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.
diephp/sequences
users-au/socialite-provider
twidpay/twid-logger
mrnewport/laravel-plaid
mrnewport/laravel-repo
mrnewport/laravel-docsign
mrnewport/laravel-stow
mrnewport/laravel-flow
mrnewport/laravel-priceable
mrnewport/laravel-nestedterms
fredmeng/laravel-apidoc-generator
itstructure/laravel-multilingual-tools
pdustdar/elasticsearch
markupai/php-sdk
rlacerda83/lumem-microservice-shipment
uni-method/symfony-lib
tacticmedia/scim-server-bundle
neika-ai/symfony-bundle
mattleads/agent-publishing-bundle
glavweb/framework-bundle