Saturday, May 23, 2026Today's Paper

Omni Apps

Laravel Maatwebsite Excel: The Ultimate Guide to Imports & Exports
May 22, 2026 · 13 min read

Laravel Maatwebsite Excel: The Ultimate Guide to Imports & Exports

Master laravel maatwebsite excel imports and exports. Learn how to install, validate, format, and queue large datasets in Laravel 8, 9, 10, and 11.

May 22, 2026 · 13 min read
LaravelPHP DevelopmentData Processing

Introduction

When building modern web applications, handling file uploads and data downloads is a ubiquitous requirement. Whether you are generating monthly financial reports for stakeholders or enabling users to batch-upload system inventories, dealing with spreadsheet files can quickly turn into an engineering bottleneck. Fortunately, the laravel maatwebsite excel package (officially known as Laravel Excel) acts as an elegant, Laravel-flavored wrapper around PhpSpreadsheet. It simplifies exports and imports into a clean, fluent syntax that integrates seamlessly with Eloquent, Laravel's validation engine, and the queue system.

In this comprehensive guide, we will explore everything you need to master maatwebsite excel in laravel, from installation across multiple Laravel versions to advanced formatting, robust import validation, multi-sheet handling, and processing massive multi-million row datasets. Whether you are maintaining legacy systems using maatwebsite excel laravel 7, migrating projects on laravel 8 maatwebsite excel, or working on modern deployments using maatwebsite excel laravel 9 and newer (including Laravel 10 and 11), this tutorial provides the production-ready code you need.


1. Architectural Evolution: Maatwebsite Excel 2.x vs 3.1

Before diving into installation, it is helpful to understand how the architecture of this library has evolved. If you are updating a legacy application or looking up tutorials online, you will frequently find outdated guides written for Laravel Excel 2.x.

In the old 2.x version, operations were highly procedural. Code was typically crammed into controllers using heavy closures, like this:

// Outdated 2.1 syntax - Do not use this in modern projects!
Excel::create('users', function($excel) {
    $excel->sheet('Sheet 1', function($sheet) {
        $sheet->fromArray(User::all()->toArray());
    });
})->download('xlsx');

This old pattern suffered from major issues. It loaded all data directly into memory at once, causing memory leaks on moderate-sized datasets. It was also difficult to unit test and lacked encapsulation.

Laravel Excel 3.x completely reimagined this. Every import or export is now encapsulated in its own clean PHP class, called a Concern-based object. Instead of procedural configurations, you implement interfaces (known in this library as 'Concerns') like FromQuery, WithHeadings, and WithMapping. This decoupled architecture drastically increases code reuse, enables automated performance optimization (like automated chunking), and allows exports and imports to be tested in isolation.


2. Installation & Multi-Version Configuration

Setting up the package is straightforward, but composer dependency constraints can occasionally cause friction depending on which version of PHP and Laravel you are running.

Step-by-Step Installation

To add the package to your project, run the following composer command:

composer require maatwebsite/excel

This command automatically grabs the latest stable release and downloads its underlying engine, phpoffice/phpspreadsheet.

Resolving Dependency Conflicts

If you are working on older systems like maatwebsite excel laravel 7 or upgrading to newer frameworks like Laravel 10/11, you may encounter a 'Your requirements could not be resolved to an installable set of packages' error. This is often caused by mismatched PHP versions or conflicting spreadsheet dependencies. You can resolve this by running:

composer require maatwebsite/excel --with-all-dependencies

Configuration and Service Providers

In older installations (such as maatwebsite excel in laravel 8 or Laravel 7), you might need to register the service provider and facade manually if auto-discovery fails. Open config/app.php and add the following lines:

'providers' => [
    /* ... other providers ... */
    Maatwebsite\Excel\ExcelServiceProvider::class,
],

'aliases' => [
    /* ... other aliases ... */
    'Excel' => Maatwebsite\Excel\Facades\Excel::class,
]

Note: If you are using Laravel 11, provider registration has been simplified. You do not need to edit config/app.php unless you have manually disabled package auto-discovery.

To publish the configuration file to customize formatting, CSV delimiters, or default temporary storage disks, run:

php artisan vendor:publish --provider="Maatwebsite\Excel\ExcelServiceProvider" --tag=config

This creates a config/excel.php file, which allows you to fine-tune your spreadsheet behaviors.


3. Crafting Professional Exports with Custom Formatting

Many tutorials show you how to export a raw, unstyled database table, but in production, clients demand readable, professional, and well-structured reports. We will build a highly customized export class that structures data, maps columns cleanly, styles the headers, and formats cell values.

Step 1: Create the Export Class

Generate a dedicated export file using the Artisan CLI. Separating your export logic from your controller keeps your codebase clean and testable:

php artisan make:export UsersExport --model=User

This command creates a skeleton file in app/Exports/UsersExport.php.

Step 2: Implement Advanced Concerns

By default, the generator creates a class using FromCollection. While this is fine for small tables, loading thousands of Eloquent models directly into memory can easily exceed your server's memory limits. To scale efficiently, we will use the FromQuery concern. We will also implement WithHeadings for header rows, WithMapping to control column order, WithColumnFormatting for cell data formats, and WithStyles to give our sheet a polished look.

Update your UsersExport.php to match the following implementation:

namespace App\Exports;

use App\Models\User;
use Maatwebsite\Excel\Concerns\FromQuery;
use Maatwebsite\Excel\Concerns\WithHeadings;
use Maatwebsite\Excel\Concerns\WithMapping;
use Maatwebsite\Excel\Concerns\WithColumnFormatting;
use Maatwebsite\Excel\Concerns\WithStyles;
use PhpOffice\PhpSpreadsheet\Shared\Date;
use PhpOffice\PhpSpreadsheet\Style\NumberFormat;
use PhpOffice\PhpSpreadsheet\Worksheet\Worksheet;

class UsersExport implements FromQuery, WithHeadings, WithMapping, WithColumnFormatting, WithStyles
{
    /**
    * Pull the query builder instead of fetching all records into memory at once.
    * This allows Laravel Excel to chunk your records automatically under the hood.
    */
    public function query()
    {
        return User::query()->where('is_active', true);
    }

    /**
    * Define the headers of the generated Excel sheet.
    */
    public function headings(): array
    {
        return [
            'ID',
            'Full Name',
            'Email Address',
            'Registration Date',
        ];
    }

    /**
    * Map database columns to specific spreadsheet rows.
    * Use PhpSpreadsheet's helper to convert model dates into Excel-serial dates.
    */
    public function map($user): array
    {
        return [
            $user->id,
            $user->name,
            $user->email,
            Date::dateTimeToExcel($user->created_at),
        ];
    }

    /**
    * Apply correct cell formats. Column 'D' (Registration Date) is formatted as YYYY-MM-DD.
    */
    public function columnFormats(): array
    {
        return [
            'D' => NumberFormat::FORMAT_DATE_YYYYMMDD2,
        ];
    }

    /**
    * Apply elegant styling directly to our worksheet.
    */
    public function styles(Worksheet $sheet)
    {
        return [
            // Row 1 (Header) styled with bold text, larger size, and a light-gray background
            1 => [
                'font' => [
                    'bold' => true, 
                    'size' => 12
                ],
                'fill' => [
                    'fillType' => \PhpOffice\PhpSpreadsheet\Style\Fill::FILL_SOLID,
                    'startColor' => ['rgb' => 'F2F2F2']
                ]
            ],
        ];
    }
}

Step 3: Trigger the Export in Your Controller

Next, create a controller to execute this export and return it as a direct download:

namespace App\Http\Controllers;

use App\Exports\UsersExport;
use Maatwebsite\Excel\Facades\Excel;

class UserController extends Controller
{
    public function export()
    {
        return Excel::download(new UsersExport, 'active_users.xlsx');
    }
}

Add your route to routes/web.php to expose this action:

use App\Http\Controllers\UserController;

Route::get('/users/export', [UserController::class, 'export'])->name('users.export');

When you hit this route, the package compiles the query, streams it through your formatted export file, and initiates a secure browser download.

Writing Dynamic Formulas

To compute calculations dynamically within Excel rather than performing raw PHP math, you can inject formulas into your row mapping array. If you have a column for price, tax rate, and final cost, you can write Excel formulas directly into your cells:

public function map($invoice): array
{
    static $rowNumber = 1;
    $rowNumber++;

    return [
        $invoice->sku,
        $invoice->subtotal,
        $invoice->tax_rate,
        '=B' . $rowNumber . '*C' . $rowNumber // Computes the tax amount dynamically inside Excel
    ];
}

4. High-Performance Imports with Row-by-Row Data Validation

While exporting is relatively simple, executing a laravel maatwebsite excel import safely requires a lot more caution. Standard files provided by users are notorious for having missing values, invalid email formats, and unexpected data structures. If you process imports blindly, you will run into database constraints, duplicate keys, or catastrophic application crashes.

Let us build a robust importer that validates incoming rows, reports errors gracefully, and ignores invalid rows without crashing the entire import script.

Step 1: Create the Import Class

Run the following Artisan command to generate your importer:

php artisan make:import UsersImport --model=User

Step 2: Implement Validation Concerns

We will make the importer incredibly robust by applying:

  1. WithHeadingRow: Allows referencing columns by their textual header instead of numeric array indexes.
  2. WithValidation: Validates each row using standard Laravel validation syntax.
  3. SkipsOnFailure: Collects validation failures gracefully instead of throwing exceptions that stop the import execution.

Here is the production-ready code for app/Imports/UsersImport.php:

namespace App\Imports;

use App\Models\User;
use Illuminate\Support\Facades\Hash;
use Illuminate\Validation\Rule;
use Maatwebsite\Excel\Concerns\ToModel;
use Maatwebsite\Excel\Concerns\WithHeadingRow;
use Maatwebsite\Excel\Concerns\WithValidation;
use Maatwebsite\Excel\Concerns\SkipsFailures;
use Maatwebsite\Excel\Concerns\SkipsOnFailure;

class UsersImport implements ToModel, WithHeadingRow, WithValidation, SkipsOnFailure
{
    use SkipsFailures;

    /**
    * Convert each spreadsheet row into a database record.
    * Column names correspond directly to your spreadsheet headers (converted to snake_case).
    */
    public function model(array $row)
    {
        return new User([
            'name'     => $row['name'],
            'email'    => $row['email'],
            'password' => Hash::make($row['password'] ?? 'Temporary123!'),
        ]);
    }

    /**
    * Set validation rules for each row.
    * 'email' checks against standard format and enforces database uniqueness.
    */
    public function rules(): array
    {
        return [
            'name'  => 'required|string|max:100',
            'email' => 'required|email|unique:users,email',
        ];
    }

    /**
    * Custom validation messages (optional, but highly recommended for clear UX).
    */
    public function customValidationMessages()
    {
        return [
            'email.unique' => 'The email address :input is already registered in our system.',
            'name.required' => 'The name field cannot be empty.',
        ];
    }
}

Step 3: Handle Import Failures in Your Controller

When executing this import in your controller, you can catch the collected validation errors and display them on your frontend. This provides high-quality, self-service feedback to your users so they can fix their spreadsheet data and re-upload.

namespace App\Http\Controllers;

use App\Imports\UsersImport;
use Illuminate\Http\Request;
use Maatwebsite\Excel\Facades\Excel;

class UserImportController extends Controller
{
    public function import(Request $request)
    {
        // 1. Validate that a file is actually uploaded and is of the right format
        $request->validate([
            'excel_file' => 'required|mimes:xlsx,xls,csv|max:10240', // Limit to 10MB
        ]);

        $import = new UsersImport();

        // 2. Perform the import
        Excel::import($import, $request->file('excel_file'));

        // 3. Inspect validation failures
        if ($import->failures()->isNotEmpty()) {
            return back()->with('import_failures', $import->failures());
        }

        return back()->with('success', 'Excel data successfully imported!');
    }
}

Step 4: Render Failures on Your Frontend

In your Blade template, you can loop through these failures to tell the user exactly where they went wrong:

@if (session()->has('import_failures'))
    <div class="alert alert-danger">
        <h4>Some rows could not be imported:</h4>
        <ul>
            @foreach (session()->get('import_failures') as $validationFailure)
                <li>
                    <strong>Row {{ $validationFailure->row() }}:</strong> 
                    {{ implode(', ', $validationFailure->errors()) }} 
                    (Submitted values: <em>{{ json_encode($validationFailure->values()) }}</em>)
                </li>
            @endforeach
        </ul>
    </div>
@endif

This prevents bad data from corrupting your database, ensures user-friendliness, and keeps your backend secure.


5. Advanced Import Handling: Multi-Sheet Support

Often, a spreadsheet uploaded by a business team contains multiple sheets containing different types of structured data—for example, a sheet for 'Clients' and another for 'Orders'. Importing this manually cell-by-cell would be extremely tedious.

To handle multi-sheet imports cleanly, we can implement the WithMultipleSheets concern. This allows us to route separate sheets to entirely independent importer classes, keeping our codebase clean and strictly single-responsibility.

Creating the Multi-Sheet Router Class

Create a root importer class, such as app/Imports/CompanyDataImport.php:

namespace App\Imports;

use Maatwebsite\Excel\Concerns\WithMultipleSheets;

class CompanyDataImport implements WithMultipleSheets
{
    /**
    * Map sheet names to specific import classes.
    */
    public function sheets(): array
    {
        return [
            'Clients' => new ClientsImport(),
            'Orders'  => new OrdersImport(),
        ];
    }
}

In this setup, ClientsImport and OrdersImport are standard importer files implementing ToModel (and optionally WithValidation and WithHeadingRow). You trigger this import inside your controller exactly the same way:

Excel::import(new CompanyDataImport, $request->file('excel_file'));

This architecture keeps your code incredibly clean and makes handling multi-faceted business files highly manageable.


6. Scaling Imports & Exports for Massive Datasets

When you work with massive spreadsheets—such as an inventory of 200,000 products—processing everything in a single web request will trigger PHP's max_execution_time limits or exhaust the server's PHP memory allocation. To keep your application blazing fast and secure under high load, you must integrate chunking, batch inserts, and background queueing.

Optimizing Imports: Chunking and Batching

To scale imports, we combine WithChunkReading and WithBatchInserts:

  • WithChunkReading: Reads the Excel file in small, manageable pieces (e.g., 500 rows at a time). This keeps the memory usage of the underlying parser extremely low.
  • WithBatchInserts: Batches the Eloquent database queries. Instead of executing 500 individual INSERT queries, Laravel Excel wraps them into a single fast SQL statement.

Modify your import class to implement these performance-oriented interfaces:

namespace App\Imports;

use App\Models\Product;
use Maatwebsite\Excel\Concerns\ToModel;
use Maatwebsite\Excel\Concerns\WithChunkReading;
use Maatwebsite\Excel\Concerns\WithBatchInserts;

class ProductsLargeImport implements ToModel, WithChunkReading, WithBatchInserts
{
    public function model(array $row)
    {
        return new Product([
            'sku'   => $row[0],
            'title' => $row[1],
            'price' => $row[2],
        ]);
    }

    /**
    * Determine how many rows to insert in a single SQL query.
    */
    public function batchSize(): int
    {
        return 1000;
    }

    /**
    * Determine how many rows to read into memory from the file at once.
    */
    public function chunkSize(): int
    {
        return 1000;
    }
}

Optimizing Exports: Queueing with Background Jobs

For heavy database queries during exports, do not make your users wait for a loading spinner. Instead, push the process to your Laravel queue worker by implementing the ShouldQueue contract.

namespace App\Exports;

use App\Models\Invoice;
use Illuminate\Contracts\Queue\ShouldQueue;
use Maatwebsite\Excel\Concerns\FromQuery;
use Maatwebsite\Excel\Concerns\Exportable;

class InvoicesLargeExport implements FromQuery, ShouldQueue
{
    use Exportable;

    public function query()
    {
        return Invoice::query()->where('status', 'paid');
    }
}

In your controller, you can initiate the queued export like this:

public function queueExport()
{
    (new InvoicesLargeExport)->store('exports/invoices_report.xlsx', 'public');

    return back()->with('status', 'Your large export has started in the background. We will notify you when it is ready for download!');
}

Behind the scenes, Laravel Excel chunks your query, dispatches multiple background jobs, compiles the final spreadsheet on your server's storage disk, and frees up your user's browser immediately.


7. Troubleshooting Common Pitfalls and FAQs

Why is Composer trying to install version 1.1 instead of 3.x?

This usually happens when your environment's PHP configuration or Laravel version has a conflict with PhpSpreadsheet. Since PhpSpreadsheet requires specific PHP extensions (like ext-zip, ext-xml, ext-gd), Composer fallback patterns might incorrectly suggest ancient versions of the library. Make sure your local and production environments have these dependencies enabled:

  • php_zip
  • php_xml
  • php_gd2
  • php_iconv

Verify this by running php -m on your terminal, then try installing again with the --with-all-dependencies flag.

Why do my imported dates look like random integers (e.g., "44197")?

Excel stores dates internally as sequential serial numbers counting the days since January 1, 1900. When importing, you need to parse this number back into a native PHP DateTime object. You can easily achieve this using PhpSpreadsheet's built-in helper:

$dateTimeObject = \PhpOffice\PhpSpreadsheet\Shared\Date::excelToDateTimeObject($row['created_at']);

Can I write directly into an existing Excel template?

Yes. If you have a pre-designed spreadsheet with complex formulas, charts, and brand styling, you can load the template, write data to specific coordinates, and download the updated version. This is accomplished using events:

use Maatwebsite\Excel\Concerns\WithEvents;
use Maatwebsite\Excel\Events\BeforeExport;

class TemplateExport implements WithEvents
{
    public function registerEvents(): array
    {
        return [
            BeforeExport::class => function(BeforeExport $event) {
                $event->writer->reopen(new \Maatwebsite\Excel\Files\LocalTemporaryFile(storage_path('templates/report.xlsx')), \Maatwebsite\Excel\Excel::XLSX);
                $sheet = $event->writer->getSheetByIndex(0);
                $sheet->setCellValue('B2', 'Dynamic Monthly Report');
            },
        ];
    }
}

How do I export simple arrays without Eloquent models?

If you are processing highly customized datasets, API results, or aggregate queries that do not map directly to Eloquent models, use the FromArray concern instead of FromQuery or FromCollection:

namespace App\Exports;

use Maatwebsite\Excel\Concerns\FromArray;

class CustomDataExport implements FromArray
{
    protected $data;

    public function __construct(array $data)
    {
        $this->data = $data;
    }

    public function array(): array
    {
        return $this->data;
    }
}

Conclusion

The laravel maatwebsite excel library is an indispensable tool in the modern Laravel ecosystem. By shifting from loading full collections to using query-based chunking, enforcing validation logic with user-friendly error views, and pushing massive tasks to background queues, you can deliver a smooth, resilient experience to your users.

Implement these patterns in your next deployment to keep your memory footprint low, your database inputs secure, and your spreadsheets looking visually flawless.

Related articles
Excel Laravel Import: The Ultimate Developer's Guide
Excel Laravel Import: The Ultimate Developer's Guide
Master the excel laravel import workflow. Learn basic model imports, model-less parsing, chunking for large files, and how it compares to CodeIgniter.
May 22, 2026 · 10 min read
Read →
How to Export Excel in Laravel: The Ultimate High-Performance Guide
How to Export Excel in Laravel: The Ultimate High-Performance Guide
Learn how to export Excel in Laravel 8 through modern versions. Master high-performance chunking, styled Blade views, imports, and queue-based background tasks.
May 22, 2026 · 11 min read
Read →
How to Convert PHP Excel to CSV and CSV to Excel: The Ultimate Developer's Guide
How to Convert PHP Excel to CSV and CSV to Excel: The Ultimate Developer's Guide
Master PHP Excel to CSV and CSV to Excel conversions. Learn to use PhpSpreadsheet, native PHP streams, and OpenSpout for low-memory, lightning-fast conversions.
May 21, 2026 · 12 min read
Read →
The Ultimate Guide to Laravel 8 Excel Import: Step-by-Step
The Ultimate Guide to Laravel 8 Excel Import: Step-by-Step
Master Laravel 8 excel import using Maatwebsite Excel. Learn to handle massive datasets with chunk reading, queue imports, custom validation, and skip errors.
May 21, 2026 · 12 min read
Read →
Free Citation Machine APA 7: Ultimate Guide & Review
Free Citation Machine APA 7: Ultimate Guide & Review
Looking for a reliable free citation machine APA 7 tool? Avoid grade-ruining errors with our expert reviews, comparison, and APA 7th edition checklist.
May 22, 2026 · 12 min read
Read →
How to Improve Blurry Photo Online: The Ultimate AI & Manual Guide
How to Improve Blurry Photo Online: The Ultimate AI & Manual Guide
Learn how to improve blurry photo online using powerful AI tools and manual adjustments. Fix motion blur, out-of-focus shots, and low-res images instantly.
May 22, 2026 · 14 min read
Read →
Summary Sentence Generator: Master 1-Sentence Summaries
Summary Sentence Generator: Master 1-Sentence Summaries
Struggling with info overload? Learn how to use a summary sentence generator to condense dense documents into clear, high-impact 1-sentence summaries instantly.
May 22, 2026 · 13 min read
Read →
Master Your Nutrition: Katy Fit Macro Calculator Guide
Master Your Nutrition: Katy Fit Macro Calculator Guide
Learn how to use the Katy Fit macro calculator to calculate your custom protein, carbs, and fats to burn fat, build muscle, or master body recomposition.
May 22, 2026 · 12 min read
Read →
Cite a Song MLA Generator: Step-by-Step Music Citation Guide
Cite a Song MLA Generator: Step-by-Step Music Citation Guide
Need to cite a track? Use our cite a song mla generator guide to format Spotify, YouTube, and vinyl music perfectly in MLA 9. Learn how here!
May 22, 2026 · 10 min read
Read →
Excel Convert CSV to XML: The Ultimate Step-by-Step Guide
Excel Convert CSV to XML: The Ultimate Step-by-Step Guide
Learn the exact steps to excel convert csv to xml. This comprehensive guide covers XML schema mapping, Power Query, and programmatic C# OpenXML options.
May 22, 2026 · 12 min read
Read →
Related articles
Related articles