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

Bagisto Bulk Upload Laravel Package

bagisto/bagisto-bulk-upload

Bagisto Bulk Upload streamlines catalog management by letting you import products in bulk via CSV from the admin panel. Reduce manual entry, update large inventories faster, and keep your Bagisto store organized with a simple, efficient upload workflow.

View on GitHub
Deep Wiki
Context7

Getting Started

Verify version fit before anything else. This module was last released against Bagisto v1.3.3 (Laravel 8–9 era). Check your composer.json constraints for bagisto/bagisto and laravel/framework first — if you are on Bagisto 2.x, stop; the admin layout, route conventions, and product model have moved and this will not drop in cleanly.

Install (standard Bagisto module path):

composer require bagisto/bagisto-bulk-upload

Then in config/app.php (or the equivalent provider list for your Bagisto version) register:

Bagisto\BulkUpload\Providers\BulkUploadServiceProvider::class,

Publish assets/config and migrate the import results tables:

php artisan vendor:publish --provider="Bagisto\BulkUpload\Providers\BulkUploadServiceProvider"
php artisan migrate

Clear caches and confirm routes register without colliding with core admin:

php artisan optimize:clear
php artisan route:list | grep -i bulk

Where to look first, in this order:

  1. src/Providers/BulkUploadServiceProvider.php — routes, views, config, migrations, and what it binds.
  2. src/Http/Controllers/ — the upload entry point, how the file is accepted and handed to the importer, and how results are returned.
  3. src/Config/ (or config/bulk-upload.php once published) — the CSV column spec. This is the contract you'll fight with day to day: column headers must map to real product attribute codes and belong to the chosen attribute family.
  4. src/Helpers/ / importer classes — the per-row read, validation, and repository write path.
  5. src/Resources/views/ — the admin Blade UI; publish and own this via resources/views/vendor/... rather than editing vendor/.

First use case to prove out (the smoke test):

Export one existing product to CSV from Bagisto, use that file as your template, strip it to the minimum viable column set (sku, name, attribute_family, categories, price, qty), create two rows — one update to an existing SKU, one net-new SKU — and import from the admin panel. If both land correctly and the results log reports clean, you have validated the whole pipeline: header mapping, attribute-family resolution, category lookup, and create-vs-update semantics. If the update row creates a duplicate instead, you have found the first thing to fix in your fork.

Implementation Patterns

Treat the admin UI as a demo, and wrap the engine. The single most valuable pattern is to separate the import engine from the admin controller on day one. Introduce an internal adapter so the UI can be replaced or supplemented by a CLI/artisan command or a scheduled job without touching the import logic:

namespace App\Catalog;

class ProductImporter
{
    public function __construct(
        private \Bagisto\BulkUpload\...\Importer $engine
    ) {}

    public function import(string $absolutePath, array $options = []): ImportResult
    {
        // normalize file, resolve attribute family, delegate to engine
        // return a typed result instead of an admin redirect
    }
}

Everything downstream — queued imports, ERP feed sync, the "run this nightly" job — hangs off this adapter, not off the controller.

SKU-as-idempotency-key pattern. Bulk import is nearly always an upsert keyed on sku. Make the create-or-update behaviour explicit in your fork rather than relying on the package's default resolution: look up by SKU first, then ProductRepository::update() or create(). Route writes through Bagisto's repository/service contracts where possible so events, validation, and index maintenance still fire — direct model writes are the fastest route to a catalogue that looks right in the admin and wrong on the storefront.

Chunked + queued processing. The package leans synchronous. For anything beyond a demo-sized file, read the file with maatwebsite/excel in your own job and push chunks onto a queue:

Excel::filter('chunk')->load($path)->chunk(200, function ($rows) {
    foreach ($rows as $row) {
        $this->importer->upsertRow($row->toArray());
    }
});

This gives you retries, progress, timeout control, and — critically — a place to put a per-chunk transaction boundary.

Category-before-product ordering. Categories referenced in a row must already exist. Either mandate that categories are created first (document it in your accepted-format spec) or resolve/create them in a pre-pass before the product loop. Doing it inline per row is a duplicate-insert bug waiting to happen.

Attribute-family-first, columns-second. Model the import as: (1) resolve attribute family, (2) diff the file headers against the attribute codes assigned to that family, (3) reject the file with a readable error listing unknown columns. This one validation gate kills the majority of "silent partial import" support tickets.

Define failure semantics per case, and write it down. For each of duplicate SKU, missing required attribute, missing category, and media failure, pick one of: reject the whole file, skip the row with a report, or roll back the batch. Default to skip-with-report for catalogue refreshes (partial success is acceptable when updating prices) and fail-fast for initial onboarding (a half-populated catalogue is worse than none). Encode the choice in config, not in scattered ifs.

Media handling as a separate pass. Download and attach images after the row is committed, ideally in its own queued step. Remote image URLs are the highest-risk surface in the whole package: allow-list schemes and hosts, enforce size caps, and never block the product write on a successful image download.

Admin UX you actually need to build. Once you own the fork, invest in three surface-level things: a downloadable, authoritative sample CSV generated from the live attribute family; a per-row results log with the offending column named; and a "dry run" mode that validates without writing. These are days of work, not weeks, and they convert most imports from a support queue into self-service.

Gotchas and Tips

Archived means you are the maintainer — pin everything. Freeze composer.lock, pin bagisto/bagisto, laravel/framework, and PHP in CI, and add a decision record naming the trigger for retiring the fork (Bagisto core shipping import, or a move off 1.x). Without the pin, an unrelated composer update can take the admin panel down.

Header parsing is where files actually fail. Strip the UTF-8 BOM (\xEF\xBB\xBF), trim whitespace, and normalize case on every header before matching. Files exported from Excel/Numbers routinely carry a BOM and trailing spaces, and a mismatch here surfaces as "column not recognized" with no useful detail. Normalize once, in your adapter, and never again.

CSV formula injection is a real finding, not a theoretical one. Any field starting with =, +, -, or @ should be neutralized on export and treated cautiously on import. If your imports are generated from external feeds and re-exported to staff, this is a compliance item, not a nicety.

XLS is a memory trap. PhpSpreadsheet loads the whole workbook into memory. Enforce a file-size cap below your php.ini upload_max_filesize/post_max_size, default to CSV, and convert XLS to CSV on ingest rather than parsing it in-request. Test with a deliberately oversized file so you know the failure is a clean validation error, not a 500.

Duplicate SKUs and concurrent imports. Two admins importing overlapping files, or a file containing the same SKU twice, will race. Serialize imports with a lock (Cache::lock('bulk-import')) and de-duplicate within the file (last-wins or reject — but pick one). Without this you get duplicate products that are painful to clean up.

Default channel/locale is a silent trap. Imported values typically land on the default channel and locale. If you operate multi-store, plan a follow-up pass that copies or maps values into other scopes, and tell merchants up front so they don't assume the import covered all storefronts.

Results log ≠ transaction log. The package's results model reports outcomes per row; it does not give you rollback. If you need "undo this import," you must either snapshot affected products before the run or make every run an idempotent re-import of the prior state. Design for the second — it's cheaper.

Silent partial success is the worst outcome. If the import reports success but half the rows were skipped, merchants lose trust immediately. Surface the skipped count in the response, log the reasons, and — in your fork — make it impossible to return "success" while rows were dropped.

Test the failure modes, not the happy path. Your CI smoke suite should cover: install + route registration, a small valid import, a large chunked import, a malformed file, a duplicate SKU, an unknown attribute column, and a rollback/skip scenario. The happy path is the least likely thing to break after a dependency bump.

Watch for core route and provider collisions after any Bagisto upgrade. Re-run php artisan route:list and check that admin bulk-upload routes still resolve to this package, not to a newer core implementation that may have superseded it — in which case the fork's retirement trigger has fired and you should delete it rather than maintain both.

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.
novasa/laravel-language-center
opencage/geocode
adolfoholzer/parameters
livenetworks/ln-starter
shitoudev/permissionmanager
johannesclimacus/laravel-tui
hocvt/seotools
vitalybaev/sypexgeo
amphibee/themosis-ignition
gito-me/fast-excel
frankyso/laravel-ipaymu
geolize-studio/laravel-chile
boomerangui/chat
cgv/localized-resource
noith/settings
kda/laravel-package
kda/filament-dynamic-navigation
bypersoft/signbox
pairus/product-data
fayne/laravel-filament-cms-core