mathielen/import-engine
A flexible PHP import engine for Laravel that turns CSV, Excel, and other source data into application models through configurable readers, mapping, validation, and processing steps, making bulk data imports repeatable, testable, and maintainable.
composer require <vendor>/<package>
php artisan vendor:publish --provider="<Vendor>\<Package>\<PackageServiceProvider>"
Importer class and the Context contract/value object. These are the two extension points you’ll touch daily.php artisan make:importer UserImporter
Then implement the required mapping/validation methods and run it from a command or controller:
(new UserImporter())->import('users.csv');
Context directly on the importer instead of threading it through every callback. Start with a simple array payload:
(new UserImporter())
->withContext(['tenant_id' => tenant()->id, 'dry_run' => false])
->import($path);
Context and resolve them inside row callbacks without relying on global state.Importer-level Context for shared state
Set Context once on the importer and read it in mapping, validation, and lifecycle hooks. This avoids repeating constructor arguments or using auth()/tenant() helpers deep in row logic.
class UserImporter extends Importer
{
public function map(array $row): array
{
return [
'tenant_id' => $this->context()->get('tenant_id'),
'email' => $row['email'],
];
}
}
Context as an immutable payload
Prefer arrays or simple DTOs for Context. Treat it as read-only during the import so queued and synchronous runs behave the same.
Queued imports
When importing via a job, ensure every value in Context is serializable. Store IDs, flags, and scalars; avoid closures, Eloquent models, or resources unless the package explicitly supports them.
Lifecycle hooks and conditional behavior
Use Context in beforeImport, afterRow, and afterImport to toggle dry-run mode, audit logging, or post-processing:
public function afterRow(array $row): void
{
if ($this->context()->get('dry_run')) {
return;
}
// persist audit record
}
Integration with commands and jobs
Build a thin artisan command that accepts options, constructs Context from those options, and dispatches either a synchronous import or a queued job. Keep context construction in one place so CLI and HTTP paths behave identically.
Testing
Assert that context is passed through to callbacks rather than mocking the entire import pipeline. For queued imports, serialize and unserialize the importer in a test to catch non-serializable context early.
Chunking and batching
Combine importer-level context with chunked reads for large files. Context remains stable across chunks, so tenant/user scoping works without re-resolving per chunk.
Context is per importer instance
Setting context on one importer does not automatically propagate to nested importers or related jobs. Pass context explicitly if you compose importers.
Serialization failures in queued imports
The most common 0.8.6-era bug is putting an Eloquent model or closure into Context and then queueing the import. Use IDs and primitive values, and resolve models inside the importer.
Constructor changes
If you override the importer constructor, call the parent constructor. Minor releases can adjust base-class initialization around context, and skipping parent::__construct() may leave context unset.
Config caching
If you publish config that contains closures or context factories, php artisan config:cache will fail. Keep context factories in service providers or dedicated classes.
Header row edge cases
CSV headers with BOM characters, duplicate column names, or trailing spaces still cause mapping surprises. Normalize headers in a custom mapping step rather than relying solely on the package’s default detection.
Memory and time limits
Large imports still benefit from chunking and unset() on large row arrays. Context does not reduce memory pressure; it only reduces coupling.
Debugging context
Log $importer->context()->all() at the start of beforeImport() when diagnosing unexpected behavior. Avoid dd() inside queued imports; use structured logs instead.
Minor-version upgrade checks
0.8.6 is a minor release with no documented breaking changes, but “various minor fixes” can alter edge-case behavior around context resolution. Run your importer test suite after upgrading, especially tests that assert default context values.
Extension points
Prefer custom Context classes or package events over monkey-patching the importer. If the package exposes middleware or hooks, attach cross-cutting concerns (logging, metrics) there rather than inside row mapping.
How can I help you explore Laravel packages today?