PHPackages                             umutcangungormus/laravel-import-export - PHPackages - PHPackages  [Skip to content](#main-content)[PHPackages](/)[Directory](/)[Categories](/categories)[Trending](/trending)[Leaderboard](/leaderboard)[Changelog](/changelog)[Analyze](/analyze)[Collections](/collections)[Log in](/login)[Sign up](/register)

1. [Directory](/)
2. /
3. [PDF &amp; Document Generation](/categories/documents)
4. /
5. umutcangungormus/laravel-import-export

ActiveLibrary[PDF &amp; Document Generation](/categories/documents)

umutcangungormus/laravel-import-export
======================================

Framework-grade, tenant-agnostic CSV/XLSX import-export pipeline for Laravel: column auto-matching, mapping templates, queued processors, and a publishable HTTP layer.

v0.1.0(today)30MITPHPPHP ^8.3CI failing

Since Jun 18Pushed todayCompare

[ Source](https://github.com/UmutCanGungormus/laravel-import-export)[ Packagist](https://packagist.org/packages/umutcangungormus/laravel-import-export)[ RSS](/packages/umutcangungormus-laravel-import-export/feed)WikiDiscussions main Synced today

READMEChangelogDependencies (10)Versions (2)Used By (0)

Laravel Import / Export
=======================

[](#laravel-import--export)

**Framework-grade, tenant-agnostic CSV/XLSX import &amp; export pipeline for Laravel.**

Column auto-matching · reusable mapping templates · queued batch processing · per-row failure tracking · a publishable HTTP layer — all decoupled from your auth, tenancy, and domain models.

[![Tests](https://github.com/UmutCanGungormus/laravel-import-export/actions/workflows/tests.yml/badge.svg)](https://github.com/UmutCanGungormus/laravel-import-export/actions/workflows/tests.yml)[![Packagist Version](https://camo.githubusercontent.com/58a2c4f3cabb4e97d1afc87c5a9b99f9519a8ed35db1458b3aa330c71c1f03d4/68747470733a2f2f696d672e736869656c64732e696f2f7061636b61676973742f762f756d757463616e67756e676f726d75732f6c61726176656c2d696d706f72742d6578706f72742e7376673f7374796c653d666c61742d737175617265)](https://packagist.org/packages/umutcangungormus/laravel-import-export)[![PHP Version](https://camo.githubusercontent.com/645e0a272b8390d29d42a0acd1992a6125f85526c71909d2d080fe13d4dd6598/68747470733a2f2f696d672e736869656c64732e696f2f7061636b61676973742f7068702d762f756d757463616e67756e676f726d75732f6c61726176656c2d696d706f72742d6578706f72742e7376673f7374796c653d666c61742d737175617265)](https://packagist.org/packages/umutcangungormus/laravel-import-export)[![Laravel](https://camo.githubusercontent.com/732074ce10d06013e5dfdd1d11a19a7532afdc6cdc96128f96315067c38597f6/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f4c61726176656c2d31312e7825323025374325323031322e782d4646324432302e7376673f7374796c653d666c61742d737175617265266c6f676f3d6c61726176656c)](https://laravel.com)[![License](https://camo.githubusercontent.com/f46bfeb2ff1c3406ce30f4df49cb56d444a51abf8ca5f17413cc9683a4c0da73/68747470733a2f2f696d672e736869656c64732e696f2f7061636b61676973742f6c2f756d757463616e67756e676f726d75732f6c61726176656c2d696d706f72742d6578706f72742e7376673f7374796c653d666c61742d737175617265)](LICENSE)

---

Table of Contents
-----------------

[](#table-of-contents)

- [Why this package](#why-this-package)
- [Features](#features)
- [Requirements](#requirements)
- [Installation](#installation)
- [Quickstart](#quickstart)
- [Configuration](#configuration)
- [Core concepts](#core-concepts)
    - [The model registry](#the-model-registry)
    - [Processors](#processors)
    - [Tenancy](#tenancy)
    - [Authorization](#authorization)
- [Processing pipeline](#processing-pipeline)
- [HTTP API](#http-api)
- [Exporting](#exporting)
- [Localization](#localization)
- [Testing](#testing)
- [Frontend](#frontend)
- [Versioning](#versioning)
- [License](#license)

---

Why this package
----------------

[](#why-this-package)

Most import/export solutions hard-wire themselves to your `User` model, your tenancy strategy, and your authorization stack — so they never quite leave the app they were born in. This package is the opposite: a **generic engine** whose every integration point is an interface you bind. Drop it into a single-tenant SaaS or a multi-tenant platform without touching its source.

It was extracted and generalized from a production multi-tenant platform, then hardened into a standalone library with a full [Pest](https://pestphp.com) suite.

Features
--------

[](#features)

- 📥 **CSV &amp; XLSX import** — deterministic streaming reader with delimiter sniffing, BOM stripping, and UTF-8 normalization. No `maatwebsite/excel` dependency required.
- 🧠 **Automatic column matching** — fuzzy header-to-field matching (Levenshtein + `similar_text` + alias/label awareness) with confidence scoring and tunable thresholds.
- 🗂️ **Mapping templates** — save a session's column mapping and reuse it; per-user limits, defaults, and validation built in.
- ⚙️ **Queued batch processing** — a planner job fans the file into a `Bus` batch of short-lived chunk jobs, so large imports survive worker timeouts and report real progress.
- 🎯 **Per-row failure tracking** — every rejected row is recorded with its reason and exportable as a failures file.
- 🏢 **Tenant-agnostic** — a single `TenantResolverContract` injects your tenant id (company, workspace, org…) into every session. Defaults to single-tenant.
- 🔐 **Auth-stack agnostic** — the package only *names* gate abilities; you bind the rules (native Gates, `spatie/laravel-permission`, Bouncer, …).
- 🌐 **Opt-in HTTP layer** — a publishable controller/request/resource set with namespaced routes, or wire your own.
- 🌍 **i18n** — English &amp; Turkish translations included and publishable.
- 🧩 **Configurable everything** — table names, disks, queue connection, FK constraints, thresholds.

Requirements
------------

[](#requirements)

VersionPHP`^8.3`Laravel`11.x` · `12.x`Installation
------------

[](#installation)

```
composer require umutcangungormus/laravel-import-export
```

The service provider is auto-discovered. Publish the config, migrations, and translations as needed:

```
# Config (required — this is where you register your models)
php artisan vendor:publish --tag=import-export-config

# Migrations
php artisan vendor:publish --tag=import-export-migrations

# Translations (optional)
php artisan vendor:publish --tag=import-export-lang

# …or everything at once
php artisan vendor:publish --tag=import-export
```

The queued batch pipeline relies on Laravel's job batching, which needs the `job_batches` table:

```
php artisan queue:batches-table
php artisan migrate
```

Quickstart
----------

[](#quickstart)

**1. Make your model importable.**

```
use Illuminate\Database\Eloquent\Model;
use Umutcangungormus\LaravelImportExport\Contracts\Exportable;
use Umutcangungormus\LaravelImportExport\Contracts\Importable;
use Umutcangungormus\LaravelImportExport\Support\HasImportExport;

class Product extends Model implements Importable, Exportable
{
    use HasImportExport; // config-driven field resolution — nothing else needed
}
```

**2. Describe it in `config/import-export.php`.**

```
'models' => [
    App\Models\Product::class => [
        'unique_by'   => ['sku'],                              // updateOrCreate key
        'processor'   => App\Imports\ProductProcessor::class,  // optional
        'fields' => [
            'sku'   => ['required' => true,  'type' => 'string',  'aliases' => ['code', 'stok kodu'], 'validation' => ['required', 'string', 'max:64']],
            'name'  => ['required' => true,  'type' => 'string',  'validation' => ['required', 'string', 'max:255']],
            'price' => ['required' => false, 'type' => 'decimal', 'validation' => ['nullable', 'numeric']],
        ],
        'export_fields' => [
            'id'    => ['accessor' => 'id'],
            'sku'   => ['accessor' => 'sku'],
            'name'  => ['accessor' => 'name'],
            'price' => ['accessor' => 'price'],
        ],
    ],
],
```

**3. Run an import** — programmatically, or via the [HTTP API](#http-api):

```
use Umutcangungormus\LaravelImportExport\Actions\ImportExport\InitializeImportAction;
use Umutcangungormus\LaravelImportExport\Actions\ImportExport\StartImportAction;
use Umutcangungormus\LaravelImportExport\Data\InitializeImportData;

// Upload → detect headers → auto-match columns
$session = app(InitializeImportAction::class)->handle(new InitializeImportData(
    model: App\Models\Product::class,
    file:  $request->file('file'),
));

// (optionally let the user adjust $session->mappings here, then…)

// Dispatch the queued batch pipeline
app(StartImportAction::class)->handle($session);
```

Progress, failures, and status are tracked on the `ImportSession` model throughout.

Configuration
-------------

[](#configuration)

`config/import-export.php` is fully documented inline. Highlights:

KeyPurposeDefault`disk` / `storage_path`Where uploaded source files live`local` / `imports``tables.*`Override table names (sessions, mappings, templates, failures, users)package defaults`foreign_keys.users`Toggle the bundled `users` FK constraint`true``batch_size`Rows per queued chunk job`500``job_timeout` / `job_tries`Per-job limits`600` / `3``queue.connection` / `queue.queue`Where jobs run (Horizon-friendly)app defaults`column_matching.auto_confirm_threshold`Score ≥ this → auto-confirmed`0.8``column_matching.suggestion_threshold`Score ≥ this → shown as a suggestion`0.3``templates.*`Enable/limit mapping templatesenabled, 50/user`tenancy.resolver`Your `TenantResolverContract` implementation`NullTenantResolver``routes.enabled` / `prefix` / `middleware`Opt-in HTTP layer`false` / `api/import-export` / `['api']``gates.*`Names of the authorization abilities to checknamespaced strings`models`The model registry (you fill this in)empty> **Tip:** For large imports, point `queue.connection` at a dedicated connection whose `retry_after` exceeds `job_timeout`, and keep `job_tries` at `1` — a half-finished bulk import must never auto-retry and double-process rows.

Core concepts
-------------

[](#core-concepts)

### The model registry

[](#the-model-registry)

Rather than annotating models, you declare each importable model's schema once in `config('import-export.models')`, keyed by FQCN. Each entry defines `fields` (with `required`, `type`, `aliases`, `validation`, `transform`, `default`), the `unique_by` key for `updateOrCreate`, an optional `processor`, and `export_fields`. The `HasImportExport` trait reads this config so your models stay clean.

### Processors

[](#processors)

Domain logic — value coercion, related-record resolution, side effects — lives in a processor implementing `ImportProcessorInterface`:

```
use Umutcangungormus\LaravelImportExport\Contracts\ImportProcessorInterface;
use Umutcangungormus\LaravelImportExport\Models\ImportSession;

class ProductProcessor implements ImportProcessorInterface
{
    /** Transform a raw row before the model is saved. */
    public function prepare(ImportSession $session, array $data): array
    {
        $data['price'] = (float) str_replace(',', '.', $data['price'] ?? 0);

        return $data;
    }

    /** Hook after each row is persisted. */
    public function after(object $model, array $data): void
    {
        // e.g. attach tags, fire events…
    }

    /**
     * OPTIONAL, order-independent post-pass (called once, after every row).
     * Ideal for self-referential FKs (manager_id, parent_id, category trees)
     * whose targets may appear later in the file than the rows referencing them.
     */
    public function afterComplete(ImportSession $session): void
    {
        // resolve deferred relationships here
    }
}
```

`afterComplete()` is intentionally **not** part of the interface signature (so existing processors keep working) — `FinalizeImportJob` calls it via `method_exists()`.

### Tenancy

[](#tenancy)

Bind a `TenantResolverContract` to scope every session to the active tenant:

```
use Illuminate\Database\Eloquent\Builder;
use Umutcangungormus\LaravelImportExport\Tenancy\TenantResolverContract;

class CompanyTenantResolver implements TenantResolverContract
{
    public function currentTenantId(): int|string|null
    {
        return auth()->user()?->company_id;
    }

    public function scopeQuery(Builder $query): Builder
    {
        return $query->where('company_id', $this->currentTenantId());
    }
}
```

```
// config/import-export.php
'tenancy' => ['resolver' => App\Support\CompanyTenantResolver::class],
```

The default `NullTenantResolver` returns `null` — perfect for single-tenant apps.

### Authorization

[](#authorization)

The package never assumes an auth stack; it only references **gate ability names** (configurable under `gates.*`). Define them however you like:

```
Gate::define('import-export.session.create', fn ($user) => $user->can('manage-imports'));
```

Processing pipeline
-------------------

[](#processing-pipeline)

```
StartImportAction
   └─ ProcessImportJob (planner)
        ├─ splits the file into a Bus batch …
        ├─ ProcessImportChunkJob ×N   (validate → transform → updateOrCreate, isolated per slice)
        └─ FinalizeImportJob          (runs processor afterComplete(), settles final status)

```

Each chunk job processes `batch_size` rows, so jobs stay short, failures are isolated to a slice, and progress is accurate. Sessions end in `Completed`, `CompletedWithErrors`, `Failed`, or `Cancelled`.

HTTP API
--------

[](#http-api)

Set `IMPORT_EXPORT_ROUTES_ENABLED=true` (or `config('import-export.routes.enabled')`) to register the bundled API under the configured prefix (`api/import-export` by default) and middleware:

MethodURIAction`GET``/sessions`List import sessions`POST``/sessions`Create a session (upload file)`GET``/sessions/{id}`Show a session`POST``/sessions/{id}/start`Start processing`DELETE``/sessions/{id}`Cancel a session`GET``/sessions/{id}/progress`Poll progress`GET``/sessions/{id}/failures`Failure summary`GET``/sessions/{id}/failures/export`Download failed rows`GET``/sessions/{id}/mappings`List column mappings`PUT``/sessions/{id}/mappings`Update mappings`GET``/sessions/{id}/mappings/suggestions`Auto-match suggestions`GET``/templates`List mapping templates`POST``/templates`Create a template`GET` `PUT` `DELETE``/templates/{id}`Show / update / delete a templatePrefer your own controllers? Leave routes disabled and call the `Actions` / `Services` directly.

Exporting
---------

[](#exporting)

```
use Umutcangungormus\LaravelImportExport\Services\ModelExportService;

// streamed CSV/XLSX download
return app(ModelExportService::class)->export(App\Models\Product::class);
```

Export columns, accessors, and relations come from the model's `export_fields` / `export_with` config. Default format and chunk size are configurable under `export.*`.

Localization
------------

[](#localization)

Translations ship under the `import-export::` namespace (English &amp; Turkish) across `errors`, `export`, `fields`, `mapping`, `session`, `status`, and `template` groups. Publish with `--tag=import-export-lang` to customize, and override field labels/aliases via `import-export::fields.*`.

Testing
-------

[](#testing)

```
composer test     # or: vendor/bin/pest
```

The suite runs on [Orchestra Testbench](https://github.com/orchestral/testbench) and covers the column matcher, file reader (CSV/XLSX/remote disk), failure handling, the full import flow, tenancy resolution, and HTTP route registration.

```
Tests:    36 passed (165 assertions)

```

Frontend
--------

[](#frontend)

A companion Vue 3 component library — drag-and-drop upload, column-mapping UI, progress, and a session manager — speaks this package's API out of the box:

➡️ **[@umut-can-gungormus/vue-import-export](https://github.com/UmutCanGungormus/vue-import-export)**

Versioning
----------

[](#versioning)

This package follows [Semantic Versioning](https://semver.org). See [CHANGELOG.md](CHANGELOG.md).

License
-------

[](#license)

The MIT License (MIT). See [LICENSE](LICENSE).

Built with care by [Umut Can Gungormus](https://github.com/UmutCanGungormus).

###  Health Score

39

—

LowBetter than 85% of packages

Maintenance100

Actively maintained with recent releases

Popularity4

Limited adoption so far

Community6

Small or concentrated contributor base

Maturity38

Early-stage or recently created project

 Bus Factor1

Top contributor holds 100% of commits — single point of failure

How is this calculated?**Maintenance (25%)** — Last commit recency, latest release date, and issue-to-star ratio. Uses a 2-year decay window.

**Popularity (30%)** — Total and monthly downloads, GitHub stars, and forks. Logarithmic scaling prevents top-heavy scores.

**Community (15%)** — Contributors, dependents, forks, watchers, and maintainers. Measures real ecosystem engagement.

**Maturity (30%)** — Project age, version count, PHP version support, and release stability.

###  Release Activity

Cadence

Unknown

Total

1

Last Release

0d ago

### Community

Maintainers

![](https://avatars.githubusercontent.com/u/63198221?v=4)[Umut Can Güngörmüş](/maintainers/UmutCanGungormus)[@UmutCanGungormus](https://github.com/UmutCanGungormus)

---

Top Contributors

[![UmutCanGungormus](https://avatars.githubusercontent.com/u/63198221?v=4)](https://github.com/UmutCanGungormus "UmutCanGungormus (11 commits)")

---

Tags

laravelexportexcelxlsxcsvimportdata-importdata-mapping

###  Code Quality

TestsPest

### Embed Badge

![Health badge](/badges/umutcangungormus-laravel-import-export/health.svg)

```
[![Health](https://phpackages.com/badges/umutcangungormus-laravel-import-export/health.svg)](https://phpackages.com/packages/umutcangungormus-laravel-import-export)
```

###  Alternatives

[larastan/larastan

Larastan - Discover bugs in your code without running it. A phpstan/phpstan extension for Laravel

6.4k51.0M7.5k](/packages/larastan-larastan)[psalm/plugin-laravel

Psalm plugin for Laravel

3325.1M337](/packages/psalm-plugin-laravel)[laravel/scout

Laravel Scout provides a driver based solution to searching your Eloquent models.

1.7k53.0M582](/packages/laravel-scout)[calebdw/larastan

Larastan - Discover bugs in your code without running it. A phpstan/phpstan extension for Laravel

15104.9k4](/packages/calebdw-larastan)[avadim/fast-excel-laravel

Lightweight and very fast XLSX Excel Spreadsheet Export/Import for Laravel

4054.7k1](/packages/avadim-fast-excel-laravel)[api-platform/laravel

API Platform support for Laravel

59156.3k11](/packages/api-platform-laravel)

PHPackages © 2026

[Directory](/)[Categories](/categories)[Trending](/trending)[Changelog](/changelog)[Analyze](/analyze)
