Registry-based data export system for Laravel with pluggable format support. Ships with CSV and JSON exporters
composer require philiprehberger/laravel-exportRegistry-based data export system for Laravel with pluggable format support. Ships with CSV, JSON, and XML exporters, plus column transformers and queued exports.
composer require philiprehberger/laravel-export
The service provider is auto-discovered via Laravel's package discovery.
Optionally publish the config file:
php artisan vendor:publish --tag=laravel-export-config
use PhilipRehberger\Export\Facades\Export;
use Illuminate\Support\Collection;
$data = collect([
['name' => 'Alice', 'email' => 'alice@example.com', 'plan' => 'pro'],
['name' => 'Bob', 'email' => 'bob@example.com', 'plan' => 'free'],
]);
$columns = [
'name' => 'Name',
'email' => 'Email Address',
'plan' => 'Plan',
];
$csvString = Export::export($data, $columns, 'csv');
$jsonString = Export::export($data, $columns, 'json');
$xmlString = Export::export($data, $columns, 'xml');
// With custom element names
$xmlString = Export::export($data, $columns, 'xml', [
'root_element' => 'users',
'item_element' => 'user',
]);
Return a download response directly from a controller:
public function download(Request $request): Response
{
$users = User::all();
$data = $users->map(fn ($u) => [
'name' => $u->name,
'email' => $u->email,
'created_at' => $u->created_at,
]);
$columns = [
'name' => 'Name',
'email' => 'Email',
'created_at' => 'Registered',
];
return Export::download($data, $columns, 'csv', 'users');
// Sends users.csv with Content-Disposition: attachment
}
return Export::stream($data, $columns, 'csv', 'users');
// Returns a StreamedResponse
Note: Filenames passed to
download()andstream()are automatically sanitized — control characters, quotes, backslashes, and forward slashes are stripped. Empty filenames fall back to"export".
Use columns() to select, rename, or compute derived columns before exporting. Keys are output column names; values are either a source column name (string) or a callable that receives the full row:
$data = collect([
['first_name' => 'Alice', 'last_name' => 'Smith', 'email' => 'alice@example.com'],
['first_name' => 'Bob', 'last_name' => 'Jones', 'email' => 'bob@example.com'],
]);
$columns = [
'Full Name' => 'Full Name',
'Email' => 'Email',
];
$csv = Export::columns([
'Full Name' => fn (array $row) => $row['first_name'] . ' ' . $row['last_name'],
'Email' => 'email',
])->export($data, $columns, 'csv');
The transformer is applied once and automatically resets after the export call.
For large datasets, dispatch the export to a background queue. The result is stored to a Laravel filesystem disk:
use PhilipRehberger\Export\PendingExport;
$pending = Export::queue($data, $columns, 'csv');
// Returns a PendingExport value object
$pending->id; // Unique export identifier
$pending->disk; // Storage disk (default: 'local')
$pending->path; // File path on disk (auto-generated or custom)
$pending->status(); // 'pending' or 'completed'
Customize the disk, path, and options:
$pending = Export::queue($data, $columns, 'json', 's3', 'reports/monthly.json', [
'pretty_print' => false,
]);
Provide an optional callback that runs after the export completes:
$pending = Export::queue($data, $columns, 'csv', 'local', null, [], function (string $path, string $disk) {
// Notify user, send email, etc.
});
Implement ExportableInterface on your Eloquent model to let the service derive columns and filename automatically:
use PhilipRehberger\Export\Contracts\ExportableInterface;
class User extends Model implements ExportableInterface
{
public function toExportArray(): array
{
return [
'name' => $this->name,
'email' => $this->email,
'plan' => $this->plan, // enums are auto-unwrapped
'created_at' => $this->created_at, // Carbon dates are auto-formatted
];
}
public static function getExportColumns(): array
{
return [
'name' => 'Name',
'email' => 'Email Address',
'plan' => 'Plan',
'created_at' => 'Registered',
];
}
public static function getExportFilename(): string
{
return 'users-export';
}
}
Then export a collection of models:
$users = User::where('active', true)->get();
// Returns the CSV string
$csv = Export::exportModels($users, 'csv');
// Returns a download Response, filename derived from model
return Export::downloadModels($users, 'csv');
// Override filename
return Export::downloadModels($users, 'csv', 'active-users');
AbstractExportFormat handles these transformations automatically inside toExportArray or when using raw collections:
| Type | Transformation |
|---|---|
BackedEnum | ->value |
Carbon\Carbon | ->toDateTimeString() (Y-m-d H:i:s) |
array / object | json_encode() |
Implement ExportFormatInterface (or extend the provided AbstractExportFormat base class):
use Illuminate\Support\Collection;
use PhilipRehberger\Export\Formats\AbstractExportFormat;
class XmlExporter extends AbstractExportFormat
{
public function export(Collection $data, array $columns, array $options = []): string
{
$rows = $this->transformData($data, $columns);
$xml = new SimpleXMLElement('<export/>');
foreach ($rows as $row) {
$item = $xml->addChild('item');
foreach ($row as $key => $value) {
$item->addChild(preg_replace('/\s+/', '_', $key), htmlspecialchars((string) $value));
}
}
return $xml->asXML();
}
public function getContentType(): string { return 'application/xml'; }
public function getFileExtension(): string { return 'xml'; }
public function getFormatName(): string { return 'xml'; }
}
Register it in a service provider:
use PhilipRehberger\Export\ExportFormatRegistry;
public function boot(ExportFormatRegistry $registry): void
{
$registry->register(new XmlExporter);
}
Then use it exactly like the built-in formats:
Export::download($data, $columns, 'xml', 'report');
Export::supportsFormat('csv'); // true
Export::supportsFormat('excel'); // false (not registered)
Export::getAvailableFormats(); // ['csv', 'json', 'xml']
Export::getFormatMetadata(); // [['name'=>'csv', 'extension'=>'csv', 'contentType'=>'text/csv; charset=UTF-8'], ...]
Published at config/laravel-export.php:
return [
'csv' => [
'delimiter' => ',',
'enclosure' => '"',
'include_bom' => true, // UTF-8 BOM for Excel compatibility
'include_headers' => true,
],
'json' => [
'pretty_print' => true,
'include_metadata' => false, // wraps output in {metadata:{}, data:[]}
],
'xml' => [
'root_element' => 'items',
'item_element' => 'item',
],
];
| Option | Format | Default | Description |
|---|---|---|---|
delimiter | CSV | , | Field separator |
enclosure | CSV | " | String enclosure character |
include_bom | CSV | true | Prepend UTF-8 BOM (improves Excel compatibility) |
include_headers | CSV | true | Write column headers as the first row |
pretty_print | JSON | true | Human-readable indented output |
include_metadata | JSON | false | Wrap array in {metadata, data} envelope |
root_element | XML | items | Root element name wrapping all items |
item_element | XML | item | Element name for each row |
Options can be passed per-call to override the config defaults:
Export::export($data, $columns, 'csv', [
'delimiter' => ';',
'include_bom' => false,
]);
Export Facade / Service| Method | Description |
|---|---|
Export::export(Collection $data, array $columns, string $format, array $options = []): string | Export data to a string in the given format |
Export::download(Collection $data, array $columns, string $format, string $filename = 'export', array $options = []): Response | Return a download response |
Export::stream(Collection $data, array $columns, string $format, string $filename = 'export', array $options = []): StreamedResponse | Return a streamed download response |
Export::exportModels(Collection $models, string $format, array $options = []): string | Export a collection of ExportableInterface models |
Export::downloadModels(Collection $models, string $format, ?string $filename = null, array $options = []): Response | Download a collection of ExportableInterface models |
Export::columns(array $columns): ExportService | Set a column transformer for the next export call |
Export::queue(Collection $data, array $columns, string $format, string $disk = 'local', ?string $path = null, array $options = [], ?callable $onComplete = null): PendingExport | Queue an export for background processing |
Export::supportsFormat(string $format): bool | Check whether a format is registered |
Export::getAvailableFormats(): array | List all registered format names |
Export::getFormatMetadata(): array | List format name, extension, and content type for each format |
ExportableInterface| Method | Description |
|---|---|
toExportArray(): array | Return the model as an associative array for export |
getExportColumns(): array | Return column key → label mapping |
getExportFilename(): string | Return the default export filename (without extension) |
composer install
vendor/bin/phpunit
vendor/bin/pint --test
vendor/bin/phpstan analyse
If you find this project useful: