lephare/import-bundle
A Laravel bundle for importing data from CSV, Excel, and other sources. It provides a flexible pipeline of readers, parsers, and importers with validation, error handling, and progress tracking, making bulk imports into Eloquent models simple, testable, and repeatable.
This is a Symfony bundle (le-phare/import-bundle), not a Laravel package — the wiring you'll meet day-to-day is Symfony DI + Console + Doctrine, with the actual import logic in plain PHP services. Read it that way even in a Laravel codebase: the reusable part is the importer classes, not the bundle bootstrapping.
Minimal path:
composer require le-phare/import-bundleconfig/bundles.php (Symfony) — on Laravel you'll skip this and bind the importer classes manually in a service provider instead.php bin/console list and find the import command the bundle ships (ImportCommand). Discover the exact name rather than guessing it from docs — the bundle version you install determines it.Look first at the bundle's ImportCommand and the importer interface it consumes — that pair tells you more about intended usage than the README.
One importer per source, no shared state. Each importer should take its input (file path, iterator, or stream) as a method argument rather than constructor state, so the same service can be reused across commands and queued jobs.
Stream, don't slurp. Use SplFileObject or a generator to yield rows. Importers that file_get_contents() the whole file are the most common cause of memory blowups on real datasets.
Batch + flush + clear. If the importer writes through Doctrine, wrap work in chunks (500–1000 rows), call $em->flush() and $em->clear() per chunk. Without the clear(), the identity map grows linearly and you'll OOM long before the file ends.
Idempotent upserts. Make the importer safe to re-run: look up by a natural key, update-or-create. This lets you re-run a failed import without truncating tables, which is what you'll actually want in production.
Transaction scope per chunk, not per file. A single transaction over a 200k-row file holds locks for the whole run and rolls everything back on row 199,999. Per-chunk transactions give you a resumable import.
Integration tips. Expose importers behind your own thin command or job wrapper rather than calling the bundle command directly from cron — you get retries, logging context (file name, row offset), and a place to add auth/tenancy scoping. For Laravel, bind the importer interface in a service provider and invoke from a queued job; the bundle's console command won't be registered there.
Testing. Run importers against a small fixture file with an in-memory SQLite connection. Assert on row counts and on one updated field after a second run — that single assertion catches most idempotency bugs.
v2.2.5 is the fix for a real CI annoyance. ImportCommand was made lazy specifically so that clearing/warming the Symfony cache does not open a database connection. Before this release, cache:clear (and container compilation in CI, where no DB is reachable) could fail with a connection error purely because the command service was instantiated. If you've seen "connection refused" during cache clear on a machine with no database, that's this bug — upgrade to ^2.2.5.
Don't over-apply the lazy fix. The same release train includes a chore removing a non-necessary lazy flag. The lesson: lazy is a targeted fix for services that touch the DB at construction, not a default. Marking everything lazy hides accidental DB access at boot instead of removing it — better to keep constructors free of connection-opening work.
Constructor discipline. The root cause pattern here is a service doing real work in its constructor. Keep importer constructors to injected dependencies only; open connections and files in import()/execute(). This makes the fix in 2.2.5 unnecessary in your own code.
Framework mismatch. Copying Symfony bundle config (services.yaml tags, config/bundles.php) into a Laravel app is a common time-sink. Only the importer classes and their interfaces port cleanly; the command, DI tags, and cache-warmer behaviour do not.
Debugging. Run the command with -vvv first; if the failure is silent, the exception is usually swallowed by a per-row try/catch inside the importer — temporarily narrow the batch to a single row to surface it.
Extension points. Prefer decorating the importer service (decorates: in Symfony DI, extend/wrapper binding in Laravel) over forking the bundle, so the next patch release — this one included — applies cleanly.
How can I help you explore Laravel packages today?