phpmystic / eloquent-exporter
Export Eloquent models to CSV & Excel with relationships support.
Requires
- php: ^8.0
- illuminate/support: ^9.0|^10.0|^11.0|^12.0
- phpoffice/phpspreadsheet: ^1.29|^2.0|^3.0|^4.0|^5.0
Requires (Dev)
- orchestra/testbench: ^7.0|^8.0|^9.0|^10.0
- phpunit/phpunit: ^9.0|^10.0|^11.0
README
Export Eloquent models to CSV & Excel with a fluent API. Supports relationships, custom headers, formatting, chunking, and multi-sheet Excel files.
Requirements
- PHP 8.0+
- Laravel 9, 10, 11, or 12
Installation
composer require phpmystic/eloquent-exporter
The service provider is auto-discovered by Laravel.
Basic Usage
use PhpMystic\EloquentExporter\EloquentExporter; // Export all users to CSV EloquentExporter::for(User::class) ->columns(['name', 'email', 'created_at']) ->toCsv(storage_path('app/exports/users.csv')); // Export all users to Excel EloquentExporter::for(User::class) ->columns(['name', 'email', 'created_at']) ->toExcel(storage_path('app/exports/users.xlsx'));
From a Query
EloquentExporter::query(User::where('is_active', true)->orderBy('name')) ->columns(['name', 'email']) ->toExcel('active-users.xlsx');
Custom Headers
EloquentExporter::for(User::class) ->columns([ 'name' => 'Full Name', 'email' => 'Email Address', 'created_at' => 'Registered At', ]) ->toCsv('users.csv');
Relationships
Relations are eager-loaded automatically to prevent N+1 queries.
BelongsTo / HasOne (Dot Notation)
EloquentExporter::for(User::class) ->columns([ 'name', 'email', 'department.name' => 'Department', 'department.manager.name' => 'Manager', ]) ->toCsv('users.csv');
| Name | Department | Manager | |
|---|---|---|---|
| John | john@example.com | Engineering | Jane |
| Sara | sara@example.com | Marketing | Bob |
HasMany (Comma-Separated)
HasMany relationships are joined into a single cell by default.
EloquentExporter::for(User::class) ->columns([ 'name', 'roles.name' => 'Roles', ]) ->toCsv('users.csv');
| Name | Roles |
|---|---|
| John | Admin, Editor |
| Sara | Viewer |
Use a custom separator:
EloquentExporter::for(User::class) ->columns([ 'name', 'tags.label' => 'Tags', ]) ->separator('tags.label', ' | ') ->toCsv('users.csv');
Expand Rows
Flatten HasMany relationships so each related record becomes its own row.
EloquentExporter::for(Order::class) ->columns([ 'id' => 'Order #', 'customer.name' => 'Customer', 'items.product.name' => 'Product', 'items.quantity' => 'Qty', 'items.price' => 'Price', ]) ->expandRows('items') ->toExcel('orders.xlsx');
| Order # | Customer | Product | Qty | Price |
|---|---|---|---|---|
| 1001 | John | Keyboard | 1 | 79.99 |
| 1001 | John | Mouse | 2 | 29.99 |
| 1002 | Sara | Monitor | 1 | 399.99 |
Nested expand is also supported:
EloquentExporter::for(Company::class) ->columns([ 'name' => 'Company', 'departments.name' => 'Department', 'departments.employees.name' => 'Employee', 'departments.employees.email' => 'Email', ]) ->expandRows('departments.employees') ->toCsv('company-roster.csv');
BelongsToMany with Pivot Data
EloquentExporter::for(User::class) ->columns([ 'name', 'projects.name' => 'Project', 'projects.pivot.role' => 'Role', 'projects.pivot.joined_at' => 'Joined', ]) ->expandRows('projects') ->toExcel('team.xlsx');
Formatting Values
EloquentExporter::for(Order::class) ->columns([ 'id' => 'Order #', 'total' => 'Total', 'created_at' => 'Date', ]) ->format('total', fn ($value) => number_format($value, 2) . ' USD') ->format('created_at', fn ($value) => $value->format('Y-m-d')) ->toExcel('orders.xlsx');
Download Response
Return a download response directly from a controller. The format is auto-detected from the file extension.
public function export() { return EloquentExporter::for(User::class) ->columns(['name', 'email']) ->download('users.xlsx'); // or 'users.csv' }
Chunking
Process large datasets in chunks to reduce memory usage.
EloquentExporter::for(User::class) ->columns(['name', 'email']) ->chunk(1000) ->toCsv('all-users.csv');
Exportable Trait
Add the Exportable trait to your model for a convenient shorthand.
use PhpMystic\EloquentExporter\Exportable; class User extends Model { use Exportable; }
// From the model User::exporter() ->columns(['name', 'email']) ->toCsv('users.csv'); // From a query builder User::where('is_active', true) ->exporter() ->columns(['name', 'email']) ->download('active-users.xlsx');
Excel Sheets
Custom Sheet Name
EloquentExporter::for(User::class) ->columns(['name', 'email']) ->sheetName('Users') ->toExcel('users.xlsx');
Multiple Static Sheets
EloquentExporter::query(User::where('is_active', true)) ->columns(['name', 'email']) ->sheetName('Active Users') ->addSheet( User::where('is_active', false), ['name', 'email'], 'Inactive Users' ) ->toExcel('users.xlsx');
Dynamic Sheets (One Sheet per Record)
Generate a sheet for each record, with related data as rows.
EloquentExporter::for(User::class) ->sheetPerRecord('name') ->columns([ 'posts.title' => 'Title', 'posts.status' => 'Status', 'posts.views' => 'Views', ]) ->toExcel('user-posts.xlsx');
This creates a sheet per user (named after name), each containing that user's posts.
Use a callback for custom sheet names:
EloquentExporter::for(User::class) ->sheetPerRecord(fn (User $user) => $user->name . ' (' . $user->email . ')') ->columns([ 'posts.title' => 'Title', 'posts.status' => 'Status', ]) ->toExcel('user-posts.xlsx');
Sheet Meta
Add parent record attributes as header rows above the data table.
EloquentExporter::for(User::class) ->sheetPerRecord('name') ->sheetMeta([ 'email' => 'Email', 'department.name' => 'Department', ]) ->columns([ 'posts.title' => 'Title', 'posts.status' => 'Status', ]) ->toExcel('user-posts.xlsx');
Combining Static and Dynamic Sheets
EloquentExporter::for(User::class) ->columns(['name', 'email']) ->sheetName('Summary') ->addDynamicSheets( User::has('posts'), fn (User $user) => $user->name, ['posts.title' => 'Title', 'posts.status' => 'Status'] ) ->toExcel('full-report.xlsx');
API Reference
| Method | Description |
|---|---|
EloquentExporter::for(Model::class) |
Create exporter for a model class |
EloquentExporter::query($builder) |
Create exporter from a query builder |
->columns(array $columns) |
Set columns to export (supports dot notation and custom headers) |
->format(string $column, Closure $formatter) |
Apply a formatter callback to a column |
->separator(string $column, string $sep) |
Set separator for HasMany comma-joined values |
->expandRows(string $relation) |
Flatten a HasMany/BelongsToMany into separate rows |
->chunk(int $size) |
Process records in chunks |
->sheetName(string $name) |
Set the Excel sheet name |
->addSheet(Builder $query, array $columns, string $name) |
Add an additional static sheet |
->sheetPerRecord(string|Closure $key) |
Create one sheet per record |
->sheetMeta(array $columns) |
Add parent record meta rows to dynamic sheets |
->addDynamicSheets(Builder $query, Closure $name, array $columns) |
Add dynamic sheets alongside static ones |
->toCsv(string $path) |
Export to a CSV file |
->toExcel(string $path) |
Export to an Excel (.xlsx) file |
->download(string $filename) |
Return a download response (format auto-detected) |
License
MIT