datasuite/laravel-exporter

A fluent, memory-efficient data export package for Laravel supporting CSV, Excel, and JSON formats

Maintainers

Package info

github.com/tusharsawant2427/laravel-exporter

pkg:composer/datasuite/laravel-exporter

Statistics

Installs: 19

Dependents: 0

Suggesters: 0

Stars: 0

Open Issues: 0

v1.0.1 2026-01-27 11:02 UTC

This package is auto-updated.

Last update: 2026-02-27 11:06:24 UTC


README

A fluent, memory-efficient data export package for Laravel supporting CSV, Excel, and JSON formats with rich formatting capabilities.

Features

  • 🚀 Fluent API - Clean, chainable methods for building exports
  • 💾 Memory Efficient - Uses generators and chunking for large datasets
  • 📊 Multiple Formats - CSV, Excel (XLSX/XML), and JSON support
  • 🔄 Flexible Data Sources - Works with Eloquent queries, Collections, and arrays
  • 🎯 Column Types - Amount, Date, Percentage, Quantity with proper formatting
  • 🌍 Multi-Locale Support - Configurable number formats for any country (US, UK, EU, India, Japan, China, etc.)
  • 🎨 Conditional Coloring - Configurable colors for positive/negative amounts (optional)
  • 📋 Report Headers - Company name, title, date range, generated by (optional)
  • Totals & Subtotals - Automatic calculation of column totals (optional)
  • 🔧 Customizable - Transform rows, set headers, and configure format options
  • 📦 Zero Dependencies - Excel export works without external libraries (optional OpenSpout support)

Installation

composer require datasuite/laravel-exporter

The package will automatically register its service provider via Laravel's package auto-discovery.

Publish Configuration (Optional)

php artisan vendor:publish --tag=exporter-config

Basic Usage

Using the Facade

use LaravelExporter\Facades\Exporter;

// Export users to CSV
Exporter::make()
    ->from(User::query())
    ->toFile(storage_path('app/exports/users.csv'));

// Export with specific columns
Exporter::make()
    ->columns(['id', 'name', 'email'])
    ->from(User::query())
    ->download('users.csv');

Using the Class Directly

use LaravelExporter\Exporter;

$exporter = Exporter::make()
    ->format('xlsx')
    ->columns(['id', 'name', 'email'])
    ->headers(['ID', 'Full Name', 'Email Address'])
    ->from(User::query())
    ->toFile(storage_path('app/exports/users.xlsx'));

Data Sources

The exporter supports multiple data sources:

// Eloquent Query Builder
Exporter::make()->from(User::query());
Exporter::make()->from(User::where('active', true));

// Collections
Exporter::make()->from(collect($data));

// Arrays
Exporter::make()->from($arrayOfData);

// LazyCollections (memory efficient)
Exporter::make()->from(User::lazy());

Export Formats

CSV Export

Exporter::make()
    ->format('csv')
    ->options([
        'delimiter' => ',',
        'enclosure' => '"',
        'include_headers' => true,
        'add_bom' => true, // Excel compatibility
    ])
    ->from($data)
    ->toFile('export.csv');

Excel Export

Exporter::make()
    ->format('xlsx')
    ->options([
        'sheet_name' => 'Users',
        'include_headers' => true,
    ])
    ->from($data)
    ->toFile('export.xlsx');

Note: For native XLSX support, install OpenSpout: composer require openspout/openspout Without OpenSpout, exports will use Excel-compatible XML format.

Excel Export with Column Types

Define column types for proper formatting and optional conditional coloring:

use LaravelExporter\Facades\Exporter;

Exporter::make()
    ->format('xlsx')
    ->columns(fn($cols) => $cols
        ->string('order_number', 'Order #')
        ->date('order_date', 'Date')
        ->string('customer_name', 'Customer')
        ->amount('total_amount', 'Amount')      // Green for +ve, Red for -ve
        ->quantity('items_count', 'Items')
        ->percentage('discount', 'Discount %')
    )
    ->from(Order::query())
    ->download('orders.xlsx');

Column Types Available

Type Method Description Excel Format
String ->string() Plain text General
Integer ->integer() Whole numbers #,##0
Amount ->amount() Currency with optional conditional coloring #,##0.00 (locale-based)
Amount Plain ->amountPlain() Currency without coloring #,##0.00
Percentage ->percentage() Percentage values 0.00%
Date ->date() Date values DD-MMM-YYYY
DateTime ->datetime() Date and time DD-MMM-YYYY HH:MM:SS
Boolean ->boolean() Yes/No values General
Quantity ->quantity() Numeric quantities #,##0.00

Report Headers (Optional)

Add professional headers to your exports when needed:

use LaravelExporter\Support\ReportHeader;

Exporter::make()
    ->format('xlsx')
    ->header(fn($h) => $h
        ->company('Acme Corporation')
        ->title('Sales Report')
        ->subtitle('Monthly Summary')
        ->dateRange('01-Nov-2024', '30-Nov-2024')
        ->generatedBy('John Doe')
        ->generatedAt()
    )
    ->columns(fn($cols) => $cols
        ->string('invoice_no', 'Invoice #')
        ->amount('amount', 'Amount')
    )
    ->from($data)
    ->download('sales-report.xlsx');

Totals Row (Optional)

Automatically calculate and add totals when needed:

Exporter::make()
    ->format('xlsx')
    ->columns(fn($cols) => $cols
        ->string('product', 'Product')
        ->quantity('qty', 'Quantity')
        ->amount('price', 'Price')
        ->amount('total', 'Total')
    )
    ->withTotals(['qty', 'price', 'total'])  // Columns to sum
    ->totalsLabel('GRAND TOTAL')
    ->from($data)
    ->download('products.xlsx');

Multi-Locale Support & Conditional Coloring

Configure locale-specific number formatting for any country:

// US format (default): 1,234,567.00
Exporter::make()
    ->format('xlsx')
    ->locale('en_US')
    ->from($data)
    ->download('report-us.xlsx');

// European format: 1.234.567,00
Exporter::make()
    ->format('xlsx')
    ->locale('de_DE')
    ->from($data)
    ->download('report-de.xlsx');

// Indian format: 12,34,567.00
Exporter::make()
    ->format('xlsx')
    ->locale('en_IN')
    ->from($data)
    ->download('report-in.xlsx');

// With optional conditional coloring (green/red for +ve/-ve)
Exporter::make()
    ->format('xlsx')
    ->locale('en_US')
    ->conditionalColoring(true)    // Optional - enable colored amounts
    ->columns(fn($cols) => $cols
        ->string('account', 'Account')
        ->amount('debit', 'Debit')
        ->amount('credit', 'Credit')
        ->amount('balance', 'Balance')
    )
    ->from($ledgerEntries)
    ->download('ledger.xlsx');

Supported Locales (Configurable)

Locale Country Number Format Currency
en_US United States 1,234,567.00 $
en_GB United Kingdom 1,234,567.00 £
en_IN India 12,34,567.00
de_DE Germany 1.234.567,00
fr_FR France 1 234 567,00
ja_JP Japan 1,234,567 ¥
zh_CN China 1,234,567.00 ¥

Add more locales in config/exporter.php.

JSON Export

Exporter::make()
    ->format('json')
    ->options([
        'pretty_print' => true,
        'wrap_in_object' => true,
        'data_key' => 'users',
        'include_metadata' => true,
    ])
    ->from($data)
    ->toFile('export.json');

Column Selection

Simple Columns

Exporter::make()
    ->columns(['id', 'name', 'email'])
    ->from(User::query());

Column Aliases

Exporter::make()
    ->columns([
        'User ID' => 'id',
        'Full Name' => 'name',
        'Email Address' => 'email',
    ])
    ->from(User::query());

Nested Columns (Dot Notation)

Exporter::make()
    ->columns([
        'id',
        'name',
        'department.name', // Access related model
    ])
    ->from(User::with('department'));

Custom Headers

Exporter::make()
    ->columns(['id', 'name', 'email'])
    ->headers(['User ID', 'Full Name', 'Email Address'])
    ->from(User::query());

Row Transformation

Transform each row before export:

Exporter::make()
    ->transformRow(function (array $row, $originalItem) {
        $row['name'] = strtoupper($row['name']);
        $row['status'] = $originalItem->isActive() ? 'Active' : 'Inactive';
        return $row;
    })
    ->from(User::query());

Output Methods

Save to File

Exporter::make()
    ->from($data)
    ->toFile(storage_path('app/exports/data.csv'));

Download Response

return Exporter::make()
    ->from($data)
    ->download('data.csv');

Stream Response (Memory Efficient)

return Exporter::make()
    ->from($data)
    ->stream('data.csv');

Get as String

$content = Exporter::make()
    ->from($data)
    ->toString();

Using the Exportable Trait

Add export functionality directly to your models:

use LaravelExporter\Traits\Exportable;

class User extends Model
{
    use Exportable;

    // Optional: Define default exportable columns
    protected array $exportable = ['id', 'name', 'email'];

    // Optional: Define default headers
    protected array $exportHeaders = ['ID', 'Full Name', 'Email'];
}

Then use it like this:

// Export with model defaults
User::query()->export()->toFile('users.csv');

// Export with custom columns
User::where('active', true)
    ->export(['id', 'name'])
    ->download('active-users.csv');

// Quick export all
User::exportAll('csv', storage_path('users.csv'));

Controller Example

use LaravelExporter\Facades\Exporter;

class ExportController extends Controller
{
    public function exportUsers(Request $request)
    {
        $format = $request->get('format', 'csv');

        return Exporter::make()
            ->format($format)
            ->columns(['id', 'name', 'email', 'created_at'])
            ->headers(['ID', 'Name', 'Email', 'Registered At'])
            ->from(User::query())
            ->download("users.{$format}");
    }
}

Memory Optimization

For large datasets, the package automatically uses:

  • Generators - Data is processed one row at a time
  • Lazy Collections - Eloquent queries use lazy() for memory efficiency
  • Chunking - Configure chunk size for optimal performance
Exporter::make()
    ->chunkSize(500) // Process 500 rows at a time
    ->from(User::query())
    ->toFile('large-export.csv');

Configuration

Publish the config file to customize defaults:

// config/exporter.php
return [
    'default_format' => 'csv',
    'chunk_size' => 1000,

    'csv' => [
        'delimiter' => ',',
        'enclosure' => '"',
        'include_headers' => true,
        'add_bom' => true,
    ],

    'excel' => [
        'include_headers' => true,
        'sheet_name' => 'Sheet1',
    ],

    'json' => [
        'pretty_print' => false,
        'wrap_in_object' => false,
    ],
];

Requirements

  • PHP 8.1+
  • Laravel 10.x or 11.x

Optional Dependencies

  • openspout/openspout - For native XLSX file support
  • phpoffice/phpspreadsheet - For advanced Excel features (formulas, conditional formatting, cell merging)

Maatwebsite Excel-Style Exports (NEW!)

If you're familiar with Maatwebsite Excel, you'll feel right at home! We now support the same interface-based Concerns pattern for cleaner, reusable export classes.

Quick Start

// Download export
use App\Exports\UsersExport;
use LaravelExporter\Facades\Excel;

return Excel::download(new UsersExport, 'users.xlsx');

Creating an Export Class

Simple Export (FromCollection)

<?php

namespace App\Exports;

use App\Models\User;
use LaravelExporter\Concerns\FromCollection;
use LaravelExporter\Concerns\WithHeadings;
use LaravelExporter\Concerns\Exportable;

class UsersExport implements FromCollection, WithHeadings
{
    use Exportable;

    public function collection()
    {
        return User::all();
    }

    public function headings(): array
    {
        return ['ID', 'Name', 'Email', 'Created At'];
    }
}

Query-Based Export (FromQuery) - Memory Efficient

<?php

namespace App\Exports;

use App\Models\Product;
use Illuminate\Database\Eloquent\Builder;
use LaravelExporter\Concerns\FromQuery;
use LaravelExporter\Concerns\WithHeadings;
use LaravelExporter\Concerns\WithMapping;
use LaravelExporter\Concerns\ShouldAutoSize;
use LaravelExporter\Concerns\Exportable;

class ProductsExport implements FromQuery, WithHeadings, WithMapping, ShouldAutoSize
{
    use Exportable;

    public function query(): Builder
    {
        return Product::query()->orderBy('category');
    }

    public function headings(): array
    {
        return ['SKU', 'Name', 'Category', 'Price', 'Stock'];
    }

    public function map($product): array
    {
        return [
            $product->sku,
            $product->name,
            $product->category,
            '' . number_format($product->price, 2),
            $product->stock_quantity,
        ];
    }
}

Full-Featured Export with Styles

<?php

namespace App\Exports;

use App\Models\Order;
use Illuminate\Database\Eloquent\Builder;
use PhpOffice\PhpSpreadsheet\Worksheet\Worksheet;
use PhpOffice\PhpSpreadsheet\Style\NumberFormat;
use LaravelExporter\Concerns\FromQuery;
use LaravelExporter\Concerns\WithHeadings;
use LaravelExporter\Concerns\WithMapping;
use LaravelExporter\Concerns\WithStyles;
use LaravelExporter\Concerns\WithColumnFormatting;
use LaravelExporter\Concerns\WithTotals;
use LaravelExporter\Concerns\WithReportHeader;
use LaravelExporter\Concerns\Exportable;
use LaravelExporter\Support\ReportHeader;

class OrdersExport implements
    FromQuery,
    WithHeadings,
    WithMapping,
    WithStyles,
    WithColumnFormatting,
    WithTotals,
    WithReportHeader
{
    use Exportable;

    public function query(): Builder
    {
        return Order::query()->with('user')->limit(10000);
    }

    public function headings(): array
    {
        return ['Order #', 'Customer', 'Status', 'Total', 'Date'];
    }

    public function map($order): array
    {
        return [
            $order->order_number,
            $order->user->name,
            ucfirst($order->status),
            $order->total,
            $order->created_at->format('Y-m-d'),
        ];
    }

    public function styles(Worksheet $sheet): array
    {
        return [
            1 => ['font' => ['bold' => true]],
            'A' => ['font' => ['bold' => true]],
        ];
    }

    public function columnFormats(): array
    {
        return [
            'D' => NumberFormat::FORMAT_NUMBER_COMMA_SEPARATED1,
        ];
    }

    public function totalColumns(): array
    {
        return ['Total'];
    }

    public function totalLabel(): string
    {
        return 'GRAND TOTAL';
    }

    public function reportHeader(): ReportHeader
    {
        return ReportHeader::make()
            ->title('Orders Report')
            ->addLine('Generated: ' . now()->format('d-M-Y'));
    }
}

Available Concerns

Concern Description
FromCollection Use a Laravel Collection as data source
FromQuery Use an Eloquent Builder (memory efficient with cursor)
FromArray Use a plain PHP array as data source
FromGenerator Use a Generator for custom iteration
WithHeadings Add column headers to export
WithMapping Transform each row before exporting
WithChunkReading Process data in chunks for very large datasets
WithColumnFormatting Apply number/date formats (requires PhpSpreadsheet)
WithColumnWidths Set fixed column widths
WithStyles Apply cell styles (requires PhpSpreadsheet)
ShouldAutoSize Auto-size columns to fit content
WithTitle Set worksheet title
WithMultipleSheets Export multiple sheets in one file
WithTotals Add a totals/summary row
WithReportHeader Add report header block
WithColumnDefinitions Use column type definitions
WithConditionalColoring Enable conditional coloring
WithFreezeRow Freeze header row
WithAutoFilter Add auto-filter dropdown
WithEvents Register event hooks

Chunked Reading for Large Datasets

For very large datasets (100K+ rows), use WithChunkReading to process data in batches:

use LaravelExporter\Concerns\FromQuery;
use LaravelExporter\Concerns\WithChunkReading;
use LaravelExporter\Concerns\Exportable;

class LargeExport implements FromQuery, WithChunkReading
{
    use Exportable;

    public function query(): Builder
    {
        return Order::query();
    }

    public function chunkSize(): int
    {
        return 1000; // Process 1000 rows at a time
    }
}

Memory Comparison:

Method Memory Usage Best For
FromCollection High (loads all) Small datasets (<1K rows)
FromQuery (cursor) Medium Medium datasets (1K-50K rows)
FromQuery + WithChunkReading Low Large datasets (50K+ rows)

Using the Exportable Trait

The Exportable trait adds convenient methods to your export class:

use App\Exports\UsersExport;

// From within a controller
return (new UsersExport)->download('users.xlsx');

// Or store to disk
(new UsersExport)->store('exports/users.xlsx', 'local');

Multiple Sheets

<?php

namespace App\Exports;

use LaravelExporter\Concerns\WithMultipleSheets;
use LaravelExporter\Concerns\Exportable;

class SalesReportExport implements WithMultipleSheets
{
    use Exportable;

    public function sheets(): array
    {
        return [
            'Products' => new ProductsExport(),
            'Orders' => new OrdersExport(),
        ];
    }
}

Comparison: Fluent API vs Concerns

Both styles are supported! Choose what works best for your use case:

Fluent API Concerns Pattern
Quick, inline exports Reusable export classes
Good for simple exports Better for complex exports
Less boilerplate More organized
Exporter::make()->from($data) Excel::download(new Export)

Importing Data (Maatwebsite-Style)

The package also supports importing data from CSV, Excel, and JSON files using the same Concerns pattern as Maatwebsite Excel.

Basic Import

use LaravelExporter\Facades\Excel;
use App\Imports\UsersImport;

// Import from file
Excel::import(new UsersImport, 'users.xlsx');

// Import from uploaded file
Excel::import(new UsersImport, $request->file('file'));

// Import from storage disk
Excel::import(new UsersImport, 'imports/users.xlsx', 's3');

Creating an Import Class

<?php

namespace App\Imports;

use App\Models\User;
use LaravelExporter\Concerns\ToModel;
use LaravelExporter\Concerns\WithHeadingRow;
use LaravelExporter\Concerns\WithValidation;
use LaravelExporter\Concerns\Importable;

class UsersImport implements ToModel, WithHeadingRow, WithValidation
{
    use Importable;

    public function model(array $row): User
    {
        return new User([
            'name' => $row['name'],
            'email' => $row['email'],
            'password' => bcrypt($row['password']),
        ]);
    }

    public function headingRow(): int
    {
        return 1;
    }

    public function rules(): array
    {
        return [
            'name' => 'required|string|max:255',
            'email' => 'required|email|unique:users,email',
        ];
    }

    public function customValidationMessages(): array
    {
        return [];
    }

    public function customValidationAttributes(): array
    {
        return [];
    }
}

Import Concerns

Concern Description
ToModel Convert each row to an Eloquent model
ToCollection Process all rows as a Collection
ToArray Process all rows as an array
OnEachRow Process each row individually
WithHeadingRow Use first row as array keys
WithValidation Validate each row
WithBatchInserts Insert models in batches
WithUpserts Update existing or create new
WithChunkReading Read file in chunks
SkipsOnError Skip rows that cause errors
SkipsOnFailure Skip rows that fail validation
WithStartRow Start reading from specific row
WithLimit Limit number of rows
WithColumnLimit Limit columns to read
WithCalculatedFormulas Get formula results
WithMappedCells Read specific cells
WithMultipleSheets Handle multiple sheets

Import with Upserts (Update or Create)

use LaravelExporter\Concerns\ToModel;
use LaravelExporter\Concerns\WithUpserts;
use LaravelExporter\Concerns\WithBatchInserts;

class ProductsImport implements ToModel, WithUpserts, WithBatchInserts
{
    public function model(array $row): Product
    {
        return new Product([
            'sku' => $row['sku'],
            'name' => $row['name'],
            'price' => $row['price'],
        ]);
    }

    public function uniqueBy(): string
    {
        return 'sku'; // Update if SKU exists
    }

    public function batchSize(): int
    {
        return 500; // Insert 500 at a time
    }
}

Handling Validation Failures

use LaravelExporter\Concerns\WithValidation;
use LaravelExporter\Concerns\SkipsOnFailure;
use LaravelExporter\Imports\Failure;

class UsersImport implements ToModel, WithValidation, SkipsOnFailure
{
    protected array $failures = [];

    public function rules(): array
    {
        return ['email' => 'required|email|unique:users'];
    }

    public function onFailure(Failure ...$failures): void
    {
        $this->failures = array_merge($this->failures, $failures);
    }

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

// Usage
$import = new UsersImport;
Excel::import($import, 'users.xlsx');

foreach ($import->getFailures() as $failure) {
    echo "Row {$failure->row()}: " . implode(', ', $failure->errors());
}

ToCollection Import

use Illuminate\Support\Collection;
use LaravelExporter\Concerns\ToCollection;
use LaravelExporter\Concerns\WithHeadingRow;

class SalesDataImport implements ToCollection, WithHeadingRow
{
    protected array $summary = [];

    public function collection(Collection $collection): void
    {
        $this->summary = [
            'total_rows' => $collection->count(),
            'total_revenue' => $collection->sum('amount'),
            'average_order' => $collection->avg('amount'),
        ];
    }

    public function headingRow(): int
    {
        return 1;
    }

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

Converting to Array/Collection

// Get raw data as array
$rows = Excel::toArray(new UsersImport, 'users.xlsx');

// Get as Collection
$collection = Excel::toCollection(new UsersImport, 'users.xlsx');

Multi-Sheet Import

use LaravelExporter\Concerns\WithMultipleSheets;

class WorkbookImport implements WithMultipleSheets
{
    public function sheets(): array
    {
        return [
            0 => new UsersImport(),      // First sheet
            1 => new ProductsImport(),   // Second sheet
            // Or by name:
            // 'Users' => new UsersImport(),
        ];
    }
}

Using the Importable Trait

use LaravelExporter\Concerns\Importable;

class UsersImport implements ToModel
{
    use Importable;
    // ...
}

// Usage
$import = new UsersImport;
$import->import('users.xlsx');

// Or
$array = $import->toArray('users.xlsx');
$collection = $import->toCollection('users.xlsx');

Import Result

$result = Excel::import(new UsersImport, 'users.xlsx');

echo "Total rows: " . $result->totalRows();
echo "Imported: " . $result->importedRows();
echo "Skipped: " . $result->skippedRows();
echo "Failed: " . $result->failedRows();
echo "Success rate: " . $result->successRate() . "%";
echo "Duration: " . $result->duration() . "s";
echo "Memory: " . $result->peakMemoryFormatted();

if ($result->errors()->hasFailures()) {
    foreach ($result->errors()->failures() as $failure) {
        // Handle failures
    }
}

Supported File Formats

Format Extension Reader
CSV .csv, .txt, .tsv Native (streaming)
Excel .xlsx, .xls OpenSpout (streaming)
JSON .json Native

License

MIT License