Export action
Overview
Filament v3.2 introduced a prebuilt action that is able to export rows to a CSV or XLSX file. When the trigger button is clicked, a modal asks for the columns that they want to export, and what they should be labeled. This feature uses job batches and database notifications, so you need to publish those migrations from Laravel. Also, you need to publish the migrations for tables that Filament uses to store information about exports:
php artisan queue:batches-table
php artisan notifications:table
php artisan vendor:publish --tag=filament-actions-migrations
php artisan migrate
If you're using PostgreSQL, make sure that the
data
column in the notifications migration is usingjson()
:$table->json('data')
.
If you're using UUIDs for your
User
model, make sure that yournotifiable
column in the notifications migration is usinguuidMorphs()
:$table->uuidMorphs('notifiable')
.
You may use the ExportAction
like so:
use App\Filament\Exports\ProductExporter;
use Filament\Actions\ExportAction;
ExportAction::make()
->exporter(ProductExporter::class)
If you want to add this action to the header of a table instead, you can use Filament\Tables\Actions\ExportAction
:
use App\Filament\Exports\ProductExporter;
use Filament\Tables\Actions\ExportAction;
use Filament\Tables\Table;
public function table(Table $table): Table
{
return $table
->headerActions([
ExportAction::make()
->exporter(ProductExporter::class)
]);
}
Or if you want to add it as a table bulk action, so that the user can choose which rows to export, they can use Filament\Tables\Actions\ExportBulkAction
:
use App\Filament\Exports\ProductExporter;
use Filament\Tables\Actions\ExportBulkAction;
use Filament\Tables\Table;
public function table(Table $table): Table
{
return $table
->bulkActions([
ExportBulkAction::make()
->exporter(ProductExporter::class)
]);
}
The "exporter" class needs to be created to tell Filament how to export each row.
Creating an exporter
To create an exporter class for a model, you may use the make:filament-exporter
command, passing the name of a model:
php artisan make:filament-exporter Product
This will create a new class in the app/Filament/Exports
directory. You now need to define the columns that can be exported.
Automatically generating exporter columns
If you'd like to save time, Filament can automatically generate the columns for you, based on your model's database columns, using --generate
:
php artisan make:filament-exporter Product --generate
Defining exporter columns
To define the columns that can be exported, you need to override the getColumns()
method on your exporter class, returning an array of ExportColumn
objects:
use Filament\Actions\Exports\ExportColumn;
public function getColumns(): array
{
return [
ExportColumn::make('name'),
ExportColumn::make('sku')
->label('SKU'),
ExportColumn::make('price'),
];
}
Customizing the label of an export column
The label for each column will be generated automatically from its name, but you can override it by calling the label()
method:
use Filament\Actions\Exports\ExportColumn;
ExportColumn::make('sku')
->label('SKU')
Configuring the default column selection
By default, all columns will be selected when the user is asked which columns they would like to export. You can customize the default selection state for a column with the enabledByDefault()
method:
use Filament\Actions\Exports\ExportColumn;
ExportColumn::make('description')
->enabledByDefault(false)
Disabling column selection
By default, user will be asked which columns they would like to export. You can disable this functionality using columnMapping(false)
:
use App\Filament\Exports\ProductExporter;
ExportAction::make()
->exporter(ProductExporter::class)
->columnMapping(false)
Calculated export column state
Sometimes you need to calculate the state of a column, instead of directly reading it from a database column.
By passing a callback function to the state()
method, you can customize the returned state for that column based on the $record
:
use App\Models\Order;
use Filament\Actions\Exports\ExportColumn;
ExportColumn::make('amount_including_vat')
->state(function (Order $record): float {
return $record->amount * (1 + $record->vat_rate);
})
Formatting the value of an export column
You may instead pass a custom formatting callback to formatStateUsing()
, which accepts the $state
of the cell, and optionally the Eloquent $record
:
use Filament\Actions\Exports\ExportColumn;
ExportColumn::make('status')
->formatStateUsing(fn (string $state): string => __("statuses.{$state}"))
If there are multiple values in the column, the function will be called for each value.
Limiting text length
You may limit()
the length of the cell's value:
use Filament\Actions\Exports\ExportColumn;
ExportColumn::make('description')
->limit(50)
Limiting word count
You may limit the number of words()
displayed in the cell:
use Filament\Actions\Exports\ExportColumn;
ExportColumn::make('description')
->words(10)
Adding a prefix or suffix
You may add a prefix()
or suffix()
to the cell's value:
use Filament\Actions\Exports\ExportColumn;
ExportColumn::make('domain')
->prefix('https://')
->suffix('.com')
Exporting multiple values in a cell
By default, if there are multiple values in the column, they will be comma-separated. You may use the listAsJson()
method to list them as a JSON array instead:
use Filament\Actions\Exports\ExportColumn;
ExportColumn::make('tags')
->listAsJson()
Displaying data from relationships
You may use "dot notation" to access columns within relationships. The name of the relationship comes first, followed by a period, followed by the name of the column to display:
use Filament\Actions\Exports\ExportColumn;
ExportColumn::make('author.name')
Counting relationships
If you wish to count the number of related records in a column, you may use the counts()
method:
use Filament\Actions\Exports\ExportColumn;
ExportColumn::make('users_count')->counts('users')
In this example, users
is the name of the relationship to count from. The name of the column must be users_count
, as this is the convention that Laravel uses for storing the result.
If you'd like to scope the relationship before calculating, you can pass an array to the method, where the key is the relationship name and the value is the function to scope the Eloquent query with:
use Filament\Actions\Exports\ExportColumn;
use Illuminate\Database\Eloquent\Builder;
ExportColumn::make('users_count')->counts([
'users' => fn (Builder $query) => $query->where('is_active', true),
])
Determining relationship existence
If you simply wish to indicate whether related records exist in a column, you may use the exists()
method:
use Filament\Actions\Exports\ExportColumn;
ExportColumn::make('users_exists')->exists('users')
In this example, users
is the name of the relationship to check for existence. The name of the column must be users_exists
, as this is the convention that Laravel uses for storing the result.
If you'd like to scope the relationship before calculating, you can pass an array to the method, where the key is the relationship name and the value is the function to scope the Eloquent query with:
use Filament\Actions\Exports\ExportColumn;
use Illuminate\Database\Eloquent\Builder;
ExportColumn::make('users_exists')->exists([
'users' => fn (Builder $query) => $query->where('is_active', true),
])
Aggregating relationships
Filament provides several methods for aggregating a relationship field, including avg()
, max()
, min()
and sum()
. For instance, if you wish to show the average of a field on all related records in a column, you may use the avg()
method:
use Filament\Actions\Exports\ExportColumn;
ExportColumn::make('users_avg_age')->avg('users', 'age')
In this example, users
is the name of the relationship, while age
is the field that is being averaged. The name of the column must be users_avg_age
, as this is the convention that Laravel uses for storing the result.
If you'd like to scope the relationship before calculating, you can pass an array to the method, where the key is the relationship name and the value is the function to scope the Eloquent query with:
use Filament\Actions\Exports\ExportColumn;
use Illuminate\Database\Eloquent\Builder;
ExportColumn::make('users_avg_age')->avg([
'users' => fn (Builder $query) => $query->where('is_active', true),
], 'age')
Configuring the export formats
By default, the export action will allow the user to choose between both CSV and XLSX formats. You can use the ExportFormat
enum to customize this, by passing an array of formats to the formats()
method on the action:
use App\Filament\Exports\ProductExporter;
use Filament\Actions\Exports\Enums\ExportFormat;
ExportAction::make()
->exporter(ProductExporter::class)
->formats([
ExportFormat::Csv,
])
// or
->formats([
ExportFormat::Xlsx,
])
// or
->formats([
ExportFormat::Xlsx,
ExportFormat::Csv,
])
Alternatively, you can override the getFormats()
method on the exporter class, which will set the default formats for all actions that use that exporter:
use Filament\Actions\Exports\Enums\ExportFormat;
public function getFormats(): array
{
return [
ExportFormat::Csv,
];
}
Modifying the export query
By default, if you are using the ExportAction
with a table, the action will use the table's currently filtered and sorted query to export the data. If you don't have a table, it will use the model's default query. To modify the query builder before exporting, you can use the modifyQueryUsing()
method on the action:
use App\Filament\Exports\ProductExporter;
use Illuminate\Database\Eloquent\Builder;
ExportAction::make()
->exporter(ProductExporter::class)
->modifyQueryUsing(fn (Builder $query) => $query->where('is_active', true))
Alternatively, you can override the modifyQuery()
method on the exporter class, which will modify the query for all actions that use that exporter:
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Relations\MorphTo;
public static function modifyQuery(Builder $query): Builder
{
return $query->with([
'purchasable' => fn (MorphTo $morphTo) => $morphTo->morphWith([
ProductPurchase::class => ['product'],
ServicePurchase::class => ['service'],
Subscription::class => ['plan'],
]),
]);
}
Configuring the export filesystem
Customizing the storage disk
By default, exported files will be uploaded to the storage disk defined in the configuration file. You can also set the FILAMENT_FILESYSTEM_DISK
environment variable to change this.
If you want to use a different disk for a specific export, you can pass the disk name to the disk()
method on the action:
ExportAction::make()
->exporter(ProductExporter::class)
->fileDisk('s3')
Alternatively, you can override the getFileDisk()
method on the exporter class, returning the name of the disk:
public function getFileDisk(): string
{
return 's3';
}