---
title: "Validated preview — Imports for Laravel | Roundly"
description: "Sessions and statuses, every row validated before the commit, counts, row results with cell errors, update diffs, queued validation and duplicate modes."
url: https://roundly-consulting.com/open-source/docs/imports-for-laravel/preview
language: en
---

[All packages](https://roundly-consulting.com/open-source.md)

[Imports for Laravel](https://roundly-consulting.com/open-source/docs/imports-for-laravel.md)

# Validated preview

upload() checks the whole file (format, encoding, limits, header row), stores it and creates an ImportSession. The session holds the source, the mapping, the mode, counts, progress and status — never row values.

## Statuses

```
uploaded → needs_mapping | mapped → validating → previewed → committing → completed → rolling_back → rolled_back
                                      └ preview_failed        └ failed (resumable)      └ rollback_failed
                                                               └ cancelled
any status before the commit → discarded (discard()) | expired (imports:sweep after sessions.expire_after)
```

## Run the preview

```php
use RoundlyConsulting\Imports\DataTransferObjects\RowFilter;

$session->preview();                    // up to preview.sync_threshold data rows: now; more: ValidatePreviewJob
$session->preview(queue: true);         // force the queue (false: force inline)
$session->isValidating();               // still on the queue: poll until…
$session->isPreviewed();                // …every row is checked and the session can be committed
$session->isCommitted();                // committing or past it
$session->progress();                   // Progress(processed, total, percent, phase)
$session->counts();                     // ImportCounts
$session->previewData();                // ImportPreview(counts, updateNotice, mode, unmappedColumns)
$session->rows(RowFilter::invalid())->paginate(50);   // RowResult(rowNumber, status, outcome, errors, match, changes)
```

Every row goes through the value pipeline, Laravel validation, batched reference lookups and batched natural-key matching, window by window — inside the owner, writing nothing to your tables and calling no importer callback. Up to preview.sync\_threshold (500) data rows validate in the request; larger files validate on the queue with progress (ImportPreviewProgressed every preview.progress\_every rows).

## Counts

- total = valid + invalid + duplicates; duplicates = matched + duplicatesInFile.
- creates, updates and skips follow the mode.
- templateRows (leftover example or hint rows) and blankRows are reported apart.

## Row results

```php
use RoundlyConsulting\Imports\DataTransferObjects\RowFilter;
use RoundlyConsulting\Imports\Enums\RowStatus;

$session->rows(RowFilter::invalid())->paginate(50);
$session->rows(RowFilter::updates())->paginate(20);      // with the field diff: [{field, from, to}]
$session->rows(RowFilter::field('email'))->count();      // invalid or failed rows with an error on email
$session->rows(RowFilter::status(RowStatus::Matched))->cursor();

foreach ($session->rows(RowFilter::invalid())->cursor() as $row) {
    foreach ($row->errors as $error) {
        // $error->field, column, columnLetter, code, message (translated), value (≤ 100 characters)
    }
}
```

Row results are stored for exceptions only — invalid, duplicate, matched and later failed rows — so a clean 10,000-row file stores none. Each holds errors per cell (field, column number and letter, code, translated message, a 100-character excerpt) but no row values. RowFilter::creates() streams the file; RowFilter::updates() adds the field diff, re-read per page. Filters: all(), invalid(), duplicates(), updates(), creates(), skips(), status(...), field($name).

## Duplicate modes

| Row | Skip | Update | CreateNew |
| --- | --- | --- | --- |
| valid | create | create | create |
| matched (an existing record) | skip | update | create |
| duplicate in the file | skip | skip | skip |
| invalid | never written | never written | never written |

```php
use RoundlyConsulting\Imports\Enums\DuplicateMode;

$session->mode(DuplicateMode::Update);   // on a previewed session: re-planned without re-reading the file
$session->previewData()->updateNotice;   // translated: undo removes created records only, never updates
```

A mode the importer does not offer throws ImportModeNotSupportedException (422). On a previewed session, a mode change whose preview moved meanwhile is refused as a stale preview (ImportStateException). Whenever updates are planned, updateNotice says that undo removes only created records and never reverts updates.

## Changing things while a large file validates

Changing the mapping or the source (sheet, delimiter, header row) drops the preview and bumps preview\_version. A queued preview is unique per session and version, and every write of a run lands only while that run’s version is current — so the newer preview always wins, whichever worker finishes first. For XLSX the preview writes a working copy (a normalised CSV of the sheet) and records the offsets commit chunks start at, so commits never re-parse a workbook.

Every change before the commit — source, mapping, mode, preview, discard and expiry — re-reads the session under a row lock and is refused with ImportStateException (409) once a commit has taken it, even one that started a moment earlier from another tab or worker. It never rewrites a running commit’s plan or deletes its file.

## A lost validation

imports:sweep fails a validating session whose progress is older than sessions.stale\_after and whose job is no longer queued or running — or that is past sessions.abandon\_after — as preview\_failed, firing ImportPreviewFailed with reason abandoned. A job that turns up late writes nothing. Preview again to retry.

## 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](https://roundly-consulting.com/support-us.md)

By donating, you agree to our [donation terms](https://roundly-consulting.com/donation-terms.md).

[Support our open source work (opens in a new tab)](https://donate.stripe.com/dRmeVe8FX5PF1Qd9pXcEw00) [Join us on Patreon (opens in a new tab)](https://www.patreon.com/cw/roundly)

## 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.

[Get a quote in 48 hours](https://roundly-consulting.com/contact.md) [Browse all packages](https://roundly-consulting.com/open-source/docs/imports-for-laravel.md)
