| Install | |
|---|---|
composer require filamentphp/advanced-export |
|
| Latest Version: | v0.3.0 |
| PHP: | ^8.2 |
Advanced export functionality for Filament resources with dynamic column selection, filtering, ordering, and background processing.
Install the package via Composer:
composer require filamentphp/advanced-export
Run the installation command:
php artisan export:install
This will:
If you prefer manual setup, publish assets individually:
# Publish configuration
php artisan export:publish --config
# Publish views
php artisan export:publish --views
# Publish translations
php artisan export:publish --lang
# Publish stubs
php artisan export:publish --stubs
# Publish everything
php artisan export:publish --all
The fastest way to add export functionality to any Filament resource:
php artisan export:resource "App\Filament\Resources\ClienteResource"
This single command will:
Exportable interface, trait, and export methodsHasAdvancedExport trait and export actionThat's it! Your resource now has full export functionality.
# Force overwrite existing files
php artisan export:resource "App\Filament\Resources\ClienteResource" --force
If you prefer more control, you can set up each component individually:
php artisan export:model App\\Models\\Cliente
Or manually add the interface and methods:
<?php
namespace App\Models;
use Filament\AdvancedExport\Contracts\Exportable;
use Filament\AdvancedExport\Traits\InteractsWithExportable;
use Illuminate\Database\Eloquent\Model;
class Cliente extends Model implements Exportable
{
use InteractsWithExportable;
public static function getExportColumns(): array
{
return [
'id' => 'ID',
'nome' => 'Name',
'email' => 'Email',
'telefone' => 'Phone',
'status' => 'Status',
'created_at' => 'Created At',
];
}
public static function getDefaultExportColumns(): array
{
return [
['field' => 'id', 'title' => 'ID'],
['field' => 'nome', 'title' => 'Full Name'],
['field' => 'email', 'title' => 'Email Address'],
['field' => 'status', 'title' => 'Status'],
];
}
}
php artisan export:views App\\Models\\Cliente
This creates:
resources/views/exports/clientes-excel.blade.php (simple export)resources/views/exports/clientes-excel-advanced.blade.php (advanced export)<?php
namespace App\Filament\Resources\Cliente\Pages;
use App\Filament\Resources\ClienteResource;
use Filament\AdvancedExport\Traits\HasAdvancedExport;
use Filament\Actions\CreateAction;
use Filament\Resources\Pages\ListRecords;
class ListClientes extends ListRecords
{
use HasAdvancedExport;
protected static string $resource = ClienteResource::class;
protected function getHeaderActions(): array
{
return [
$this->getAdvancedExportHeaderAction(),
CreateAction::make(),
];
}
}
One of the key features of this package is automatic filter support. When users apply filters to your Filament table (e.g., ?filters[status][values][0]=pending), the export will automatically respect those filters.
The package automatically:
WHERE or WHERE IN clause| Filter Type | Example URL | Query Applied |
|---|---|---|
| Single value | ?filters[status][value]=active |
WHERE status = 'active' |
| Multiple values | ?filters[status][values][0]=pending&filters[status][values][1]=active |
WHERE status IN ('pending', 'active') |
| Date range | ?filters[created_at][from]=2024-01-01&filters[created_at][until]=2024-12-31 |
WHERE created_at BETWEEN ... |
For complex filters that don't map directly to columns, override the applyCustomFilter method:
class ListClientes extends ListRecords
{
use HasAdvancedExport;
protected function applyCustomFilter($query, string $filterName, mixed $filterValue): void
{
match ($filterName) {
'has_orders' => $query->whereHas('orders'),
'premium_customer' => $query->where('total_spent', '>', 10000),
default => $this->applyGenericFilter($query, $filterName, $filterValue),
};
}
}
The configuration file is published to config/advanced-export.php:
return [
// Export limits
'limits' => [
'max_records' => 2000,
'chunk_size' => 500,
'queue_threshold' => 2000,
],
// View configuration
'views' => [
'path' => 'exports',
'simple_suffix' => '-excel',
'advanced_suffix' => '-excel-advanced',
'use_package_views' => false,
],
// Date formatting
'date_format' => 'd/m/Y H:i',
// File generation
'file' => [
'extension' => 'xlsx',
'disk' => 'public',
'directory' => 'exports',
],
// Action button appearance
'action' => [
'name' => 'export',
'label' => null,
'icon' => 'heroicon-o-arrow-down-tray',
'color' => 'success',
],
// Queue settings
'queue' => [
'enabled' => true,
'connection' => 'default',
'queue' => 'exports',
],
];

You can export relationship data simply by using the relationship name as the column key:
public static function getExportColumns(): array
{
return [
'id' => 'ID',
'declaration_number' => 'Declaration Number',
'insurer' => 'Insurer', // Will automatically load the relationship
'status' => 'Status',
];
}
The package will automatically detect and load the relationship, displaying the related model's default display value.
For more specific relationship data (like a specific attribute), use dot notation:
public static function getExportColumns(): array
{
return [
'id' => 'ID',
'insurer.name' => 'Insurer Name', // Specific attribute
'insurer.nuit' => 'Insurer NUIT', // Another attribute
'status' => 'Status',
];
}
To optimize performance, specify relationships to eager load:
class ListDeclarations extends ListRecords
{
use HasAdvancedExport;
protected function getExportRelationshipsForModel(): array
{
return ['insurer', 'payments', 'createdBy'];
}
}
Handle ordering by relationship columns:
class ListClientes extends ListRecords
{
use HasAdvancedExport;
protected function applyCustomOrdering($query, string $orderColumn, string $orderDirection): void
{
if ($orderColumn === 'insurer_name') {
$query->join('insurers', 'declarations.insurer_id', '=', 'insurers.id')
->orderBy('insurers.name', $orderDirection);
return;
}
$query->orderBy($orderColumn, $orderDirection);
}
}
If you don't want to create custom views for each model:
// config/advanced-export.php
'views' => [
'use_package_views' => true,
],
export:resource (Recommended)Complete setup for a Filament resource in one command:
# Basic usage
php artisan export:resource "App\Filament\Resources\ClienteResource"
# Force overwrite existing files
php artisan export:resource "App\Filament\Resources\ClienteResource" --force
This command:
$model propertygetPages()export:model to configure the modelexport:views to generate Blade templatesexport:installInitial package setup:
php artisan export:install
php artisan export:install --panel=admin
php artisan export:install --no-interaction
export:modelAdd export methods to a model:
php artisan export:model App\\Models\\Cliente
php artisan export:model App\\Models\\Cliente --columns=id,nome,email,created_at
php artisan export:model App\\Models\\Cliente --force
export:viewsGenerate export views for a model:
php artisan export:views App\\Models\\Cliente
php artisan export:views App\\Models\\Cliente --force
export:publishPublish package assets:
php artisan export:publish --config
php artisan export:publish --views
php artisan export:publish --stubs
php artisan export:publish --lang
php artisan export:publish --migrations
php artisan export:publish --all
php artisan export:publish --all --force
The package includes translations for:
en)pt)To add more languages, publish the translations and create new language files:
php artisan export:publish --lang
Then create resources/lang/vendor/advanced-export/{locale}/messages.php.
The package includes a powerful background export feature with automatic database notifications. This is ideal for large exports that would otherwise timeout.
Add the background export action to your ListRecords page:
use Filament\AdvancedExport\Traits\HasAdvancedExport;
class ListDeclarations extends ListRecords
{
use HasAdvancedExport;
protected function getHeaderActions(): array
{
return [
$this->getAdvancedExportHeaderAction(), // Synchronous export
$this->getBackgroundExportAction(), // Background export with notifications
CreateAction::make(),
];
}
}
ProcessExportJob is dispatched to the queueMake sure your Filament panel has database notifications enabled:
// app/Providers/Filament/AdminPanelProvider.php
public function panel(Panel $panel): Panel
{
return $panel
->default()
->id('admin')
->databaseNotifications() // Enable this!
// ...
}
Background exports require queue workers. Run both the exports queue and the default queue (for notifications):
# In separate terminals or using a process manager like Supervisor:
php artisan queue:work --queue=exports
php artisan queue:work --queue=default
Or use a single worker for both:
php artisan queue:work --queue=exports,default
You can also dispatch exports directly:
use Filament\AdvancedExport\Jobs\ProcessExportJob;
ProcessExportJob::dispatch(
modelClass: Cliente::class,
filters: $activeFilters,
fileName: 'clientes_export_2024.xlsx',
viewName: 'exports.clientes-excel-advanced',
columnsConfig: $columnsConfig,
orderColumn: 'created_at',
orderDirection: 'desc',
relationships: ['tipoCliente', 'provincia'],
userId: auth()->id() // Required for notifications
);
If you use a custom user model, configure it in config/advanced-export.php:
'user_model' => App\Models\User::class,
Background exports fully support all filter types:
| Filter Type | Automatically Handled |
|---|---|
| Direct columns | status, type |
| Relationship filters | insurer → insurer_id |
| Date ranges | created_at[from/until] |
| Multiple values | status[values][0,1,2] |
The ProcessExportJob automatically resolves relationship filter names to their actual column names (e.g., insurer → insurer_id).
The package uses Blade views to render Excel exports. You can customize these views to format data, add styling, or handle special fields.
| Type | Generated File | Purpose |
|---|---|---|
| Simple | {table}-excel.blade.php |
Exports all model attributes automatically |
| Advanced | {table}-excel-advanced.blade.php |
Exports only user-selected columns with custom titles |
Both views receive these variables:
| Variable | Type | Description |
|---|---|---|
${tableName} |
Collection | The records to export (e.g., $declarations) |
$columnsConfig |
array | User-selected columns with titles (advanced only) |
@php use Carbon\Carbon; @endphp
<table>
<thead>
<tr>
@foreach($columnsConfig as $columnConfig)
<th>{{ $columnConfig['title'] ?? 'Untitled' }}</th>
@endforeach
</tr>
</thead>
<tbody>
@foreach($declarations as $declaration)
<tr>
@foreach($columnsConfig as $columnConfig)
@php $field = $columnConfig['field'] ?? ''; @endphp
<td>
@switch($field)
{{-- Custom field handling here --}}
@default
{{ $declaration->{$field} ?? '-' }}
@endswitch
</td>
@endforeach
</tr>
@endforeach
</tbody>
</table>
Use @switch statements to format specific fields:
@switch($field)
{{-- Date formatting --}}
@case('created_at')
{{ $record->created_at?->format('d/m/Y H:i') ?? '-' }}
@break
{{-- Relationship data --}}
@case('insurer')
{{ $record->insurer->name ?? '-' }}
@break
{{-- Currency formatting --}}
@case('amount')
{{ number_format($record->amount, 2, ',', '.') }}
@break
{{-- Boolean values --}}
@case('is_active')
{{ $record->is_active ? 'Yes' : 'No' }}
@break
{{-- Enum/Status with translation --}}
@case('status')
{{ __("statuses.{$record->status}") }}
@break
{{-- Nested relationships --}}
@case('client.province')
{{ $record->client?->province?->name ?? '-' }}
@break
@default
{{ $record->{$field} ?? '-' }}
@endswitch
Here's a full example for a declarations export:
{{-- resources/views/exports/declarations-excel-advanced.blade.php --}}
@php use Carbon\Carbon; @endphp
<table>
<thead>
<tr>
@foreach($columnsConfig as $columnConfig)
<th>{{ $columnConfig['title'] ?? __('advanced-export::messages.undefined_title') }}</th>
@endforeach
</tr>
</thead>
<tbody>
@foreach($declarations as $declaration)
<tr>
@foreach($columnsConfig as $columnConfig)
@php $field = $columnConfig['field'] ?? ''; @endphp
<td>
@switch($field)
@case('id')
{{ $declaration->id ?? '-' }}
@break
@case('declaration_number')
{{ $declaration->declaration_number ?? '-' }}
@break
@case('insurer')
{{ $declaration->insurer->name ?? '-' }}
@break
@case('cif_value')
{{ number_format($declaration->cif_value ?? 0, 2, ',', '.') }}
@break
@case('status')
{{ ucfirst($declaration->status ?? '-') }}
@break
@case('created_at')
{{ $declaration->created_at?->format('d/m/Y H:i') ?? '-' }}
@break
@default
{{ $declaration->{$field} ?? '-' }}
@endswitch
</td>
@endforeach
</tr>
@endforeach
</tbody>
</table>
If you don't need custom formatting, enable the package's default views:
// config/advanced-export.php
'views' => [
'use_package_views' => true,
],
The default views automatically:
config('advanced-export.date_format')-Maatwebsite Excel supports basic HTML styling:
<table>
<thead>
<tr style="background-color: #4CAF50; color: white; font-weight: bold;">
@foreach($columnsConfig as $columnConfig)
<th>{{ $columnConfig['title'] }}</th>
@endforeach
</tr>
</thead>
<tbody>
@foreach($records as $record)
<tr style="{{ $loop->even ? 'background-color: #f2f2f2;' : '' }}">
{{-- cells --}}
</tr>
@endforeach
</tbody>
</table>
Override trait methods to customize appearance:
class ListClientes extends ListRecords
{
use HasAdvancedExport;
protected function getExportActionLabel(): string
{
return 'Download Excel';
}
protected function getExportActionIcon(): string
{
return 'heroicon-o-document-arrow-down';
}
protected function getExportActionColor(): string
{
return 'primary';
}
protected function getExportModalHeading(): string
{
return 'Configure Your Export';
}
}
Please see CHANGELOG for more information on what has changed recently.
Contributions are welcome! Please see CONTRIBUTING for details.
If you discover any security-related issues, please email anselmokossa.apk@gmail.com instead of using the issue tracker.
The MIT License (MIT). Please see License File for more information.