NewWe open-sourced 50+ Laravel packages
Custom AI apps, agents and automation — Roundly ConsultingRoundly
All packages
Imports for Laravel

The Imports facade

Everything goes through one facade, RoundlyConsulting\Imports\Facades\Imports, sugar over the ImportsManager singleton. It is also registered as the global alias Imports:

use RoundlyConsulting\Imports\Enums\{SessionStatus, TemplateFormat};
use RoundlyConsulting\Imports\Facades\Imports;

// Importers and templates
Imports::importers()->available($user, $team);
Imports::register(AssetsImporter::class);
Imports::importer('assets')->descriptor('sk');
Imports::template('assets', TemplateFormat::Xlsx, locale: 'sk')->download();

// Everything for one owner — the entry point HTTP code uses
$imports = Imports::for($team)->by($user);
$session = $imports->upload('assets', $request->file('file'));
$files = $imports->uploadFiles('documents', $request->file('files'));
$imports->sessions('assets', SessionStatus::Previewed)->paginate();
$imports->session($id)->counts();
$imports->batch($id)->summary();
$imports->mappings('assets')->all();
$imports->running('assets');

// Trusted paths, the reader and housekeeping
Imports::session($model); Imports::batch($id);
Imports::read($request->file('file'));
Imports::sweep(); Imports::prune();

The whole flow, start to finish:

$session = Imports::for($team)->by($user)->upload('assets', $request->file('file'));   // checked + stored
$session->suggestMapping();                       // MappingSuggestion (applied already when complete)
$session->map(['Inventory no' => 'asset_tag', 'Name' => 'name'])   // optional: by header text or column number
    ->saveMapping('ERP export');                  // optional: reused for identical headers
$session->mode(DuplicateMode::Update)->preview(); // ≤ 500 rows: now; more: queued (poll isPreviewed())
$session->counts();                               // ImportCounts
$session->rows(RowFilter::invalid())->paginate(50);
$batch = $session->commit();                      // a Laravel job batch
$batch->progress();
$batch->summary();
$batch->rollback();                               // within imports.undo.window

Manager methods

MethodReturnsDoes
for(Model $owner)OwnerImportsThe owner-scoped entry point — uploads, sessions, batches, saved mappings. HTTP code uses only this.
importers()Accessors\Importersall(), find(), has(), describe(), available() — discovery for a UI.
register(string ...$classes)voidRuntime registration beside imports.importers.
importer(string $key)ImporterHandledescriptor(), template(), modes() — UnknownImporterException for an unknown key.
template(string $importer, ?TemplateFormat $format = null, ?string $locale = null)TemplateA CSV or XLSX template; the format defaults to imports.templates.default_format.
session(ImportSession|int|string $session)SessionHandleTrusted path, no owner check — ImportNotFoundException when missing.
batch(ImportBatch|int|string $batch)BatchHandleThe same for a batch.
read(ImportSource|UploadedFile|string $file, string|int|null $sheet = null, ?string $delimiter = null, int $headerRow = 1)SpreadsheetFileThe standalone reader — no session, nothing stored.
prune() · sweep()PruneReport · SweepReportWhat imports:prune and imports:sweep run.
Imports::fake()ImportsFakeFacade only — the recording fake (see Testing).

container() and perform() are public on the manager but internal — perform() is the funnel every handle runs its action through, the hook the fake records with. Don’t call them from app code.

Imports::for($owner)

MethodReturnsDoes
by(Authenticatable|Model|null $actor)OwnerImportsA copy acting as $actor — LogicException when it is not an Eloquent model.
upload(string $importer, ImportSource|UploadedFile|string $file, string|int|null $sheet = null, ?string $delimiter = null, int $headerRow = 1, ?string $locale = null)SessionHandleCheck, store and open a session. authorize(Upload) runs before the file is read.
uploadFiles(string $importer, iterable<UploadedFile>|UploadedFile $files, ?string $locale = null)SessionHandleA file batch: many files or ZIP archives.
sessions(?string $importer = null, SessionStatus ...$status)Builder<ImportSession>Newest first — one importer (refused unless the actor may view it) or every importer the actor may view.
session(int|string $id) · batch(int|string $id)SessionHandle · BatchHandleAnother owner’s id is ImportNotFoundException (404).
mappings(string $importer)SavedMappingsHandleall(), find(), save(), delete().
running(string $importer)?SessionHandleThe session currently committing or rolling back.

$sheet is a name (exact, then case-insensitive) or a 1-based position among visible sheets; $headerRow is 1-based; $locale is the session locale — labels, messages and the decimal separator.

The session handle

Mutators return the handle, so a flow chains. Reads ask authorize(View) once per handle; writes ask their own operation. Source, mapping, mode, preview and discard are refused with ImportStateException once a commit holds the session:

MethodReturnsDoes
by($actor) · model() · id()SessionHandle · ImportSession · int|stringA copy acting as $actor; plain accessors.
status()SessionStatusRe-read from the database — View.
isValidating() · isPreviewed() · isCommitted()boolCommitted = committing or any later status — View.
sheets() · headers()list<SheetInfo> · list<string>A blank header reads Column C — View.
useSheet($sheet) · useDelimiter($delimiter) · useHeaderRow($row)selfChange how the stored file is read; the preview is dropped — Upload.
suggestMapping()MappingSuggestionView.
map(array|ColumnMapping $mapping) · saveMapping(string $name)selfUpload.
mode(DuplicateMode $mode)selfUpload.
preview(?bool $queue = null)selfnull: inline up to preview.sync_threshold, else queued; true / false forces it — Upload.
counts() · previewData() · progress()ImportCounts · ImportPreview · Progressprogress() has phase preview, or the batch’s while committing — View.
rows(?RowFilter $filter = null)RowQuerypaginate(), cursor(), count() — View.
errorReport(TemplateFormat $format = TemplateFormat::Xlsx, ?string $locale = null)TemplateFailing rows plus an Errors column — View.
commit()BatchHandleCommit.
cancel()selfCancels the running batch, if any — Commit.
discard()selfDeletes the file and row results — Upload.
batch()?BatchHandleView.

The batch handle

MethodReturnsDoes
by($actor) · model() · id()BatchHandle · ImportBatch · int|stringA copy acting as $actor; plain accessors.
session()SessionHandleView.
status()BatchStatusRe-read — View.
isRunning() · isFinished() · isRolledBack()boolpending/running · completed/failed/cancelled · undone — View.
progress()Progressphase commit (chunks) or rollback (created records) — View.
summary()ImportSummaryView.
records(?RecordAction $action = null)Builder<ImportedRecord>Ledger rows ordered by row number — View.
cancel() · resume()selfCommit.
rollback()selfRollback.
canRollback()boolCompleted/failed/cancelled, no rollback yet or a failed one, before undo_until — View.
rollbackReport()?RollbackReportOnce a rollback finished or failed — View.

Importers, templates and saved mappings

  • Imports::importers() — all() (every ImportDefinition), find($key), has($key), describe($key, ?$locale), available(?$user, $owner, ?$locale).
  • Imports::importer($key) — descriptor(?$locale), template(?$format, ?$locale), modes() (empty for a file importer).
  • Template — format and filename properties; download(?$filename), store($disk, $path), toTemporaryFile(?$target), contents().
  • Imports::for($owner)->mappings($importer) — all(), find($name), save($name, $columns), delete($name).
  • RowQuery ($session->rows($filter)) — paginate($perPage = 50, ?$page), cursor(), count().

Value objects, DTOs and enums

  • Definitions (Definition\): Field, NaturalKey, Ownership, ChangeDetection, FileRules, FileDuplicates, Limits, FieldChange. Callback arguments (Rows\): ImportRow, ImportContext, ImportFile. Reading (Reading\): SpreadsheetFile, Cell, ReadOptions.
  • DTOs (final readonly): ImportSource, UploadData, UploadFilesData, SaveMappingData, ColumnMapping, MappedColumn, MappingSuggestion, SuggestedColumn, SavedMapping, SheetInfo, ImportCounts, ImportPreview, RowFilter, RowResult, CellError, RecordReference, Progress, ImportSummary, RollbackReport, ImportedRecordView, ImportAudit, FileReference, ImporterDescriptor, FieldDescriptor, OptionDescriptor, PruneReport, SweepReport, DoctorFinding.
  • Models (swappable via imports.models.*): ImportSession, ImportBatch, ImportRowResult, ImportedRecord. ImportSession and ImportBatch have handle() and countOf(string $key) — one stored count (valid, creates, created, …), 0 when absent; the static ImportSession::count() is Eloquent’s row count.
  • Enums are string-backed, use enums-for-laravel’s helpers (label(), labels(), options(), values(), fromLabel(), is(), …) and translate their labels in English and Slovak:
EnumCases
SessionStatusuploaded, needs_mapping, mapped, validating, previewed, preview_failed, committing, completed, failed, cancelled, rolling_back, rolled_back, rollback_failed, discarded, expired — plus ::beforeCommit(), ::undoable(), isBeforeCommit()
BatchStatuspending, running, completed, failed, cancelled
RollbackStatusrolling_back, rolled_back, rollback_failed
RowStatus · RowOutcomevalid, invalid, duplicate_in_file, matched, failed · create, update, skip
RecordActioncreated, linked, updated
RollbackOutcomedeleted, handled, kept, kept_changed, missing, failed
DuplicateModeskip, update, create_new
ImportOperationview, upload, commit, rollback
ImportKindrows, files
FieldTypetext, integer, decimal, boolean, date, datetime, email, choice, reference
SourceFormat · TemplateFormatcsv, xlsx
CellTypetext, number, boolean, date, error
MappingSourcesaved, name, label, alias, fuzzy, manual, suggested
DelimiterSourcesniffed, hint (sep=), override
RejectionReason27 cases — see Reading files

Show your open-source love

This package is free and MIT-licensed. If it saves you time, a one-off donation or a Patreon membership keeps it maintained, tested and documented.

More ways to support, including crypto

By donating, you agree to our donation terms.

Want this built into your product?

We integrate our packages into custom Laravel and AI builds. Tell us what you're working on and we'll reply within 48 hours.