Introduction
Filament includes an 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:If you’re using PostgreSQL, make sure that the
data column in the notifications migration is using json(): $table->json('data').If you’re using UUIDs for your
User model, make sure that your notifiable column in the notifications migration is using uuidMorphs(): $table->uuidMorphs('notifiable').ExportAction like so:
Filament\Actions\ExportBulkAction:
Creating an exporter
To create an exporter class for a model, you may use themake:filament-exporter command, passing the name of a model:
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:
Defining exporter columns
To define the columns that can be exported, you need to override thegetColumns() method on your exporter class, returning an array of ExportColumn objects:
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 thelabel() method:
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 theenabledByDefault() method:
enableVisibleTableColumnsByDefault() method on the ExportAction to enable only the columns that are currently visible in the table by default. Columns that use enabledByDefault(false) will also be disabled by default:
Configuring the column selection form layout
By default, the column selection form uses a single column layout. You can change this using thecolumnMappingColumns() method, passing the number of columns you would like to use for the layout on large screens:
Disabling column selection
By default, user will be asked which columns they would like to export. You can disable this functionality usingcolumnMapping(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 thestate() method, you can customize the returned state for that column based on the $record:
As well as $record, the state() function can inject various utilities as parameters.
$record, the state() function can inject various utilities as parameters.Export column
$column
Filament\Actions\Exports\ExportColumn
The current export column instance.
Exporter
$exporter
?Filament\Actions\Exports\Exporter
The instance of the exporter class that is currently being used for exporting data.
Options
$options
array<string, mixed>
The options that were defined when the export started.
Eloquent record
$record
?Illuminate\Database\Eloquent\Model
The Eloquent record that is currently being exported.
Formatting the value of an export column
You may instead pass a custom formatting callback toformatStateUsing(), which accepts the $state of the cell, and optionally the Eloquent $record:
As well as $state, the formatStateUsing() function can inject various utilities as parameters.
$state, the formatStateUsing() function can inject various utilities as parameters.Export column
$column
Filament\Actions\Exports\ExportColumn
The current export column instance.
Exporter
$exporter
?Filament\Actions\Exports\Exporter
The instance of the exporter class that is currently being used for exporting data.
Options
$options
array<string, mixed>
The options that were defined when the export started.
Eloquent record
$record
?Illuminate\Database\Eloquent\Model
The Eloquent record that is currently being exported.
State
$state
mixed
The state to format.
Limiting text length
You maylimit() the length of the cell’s value:
As well as allowing a static value, the limit() method also accepts a function to dynamically calculate it. You can inject various utilities into the function as parameters.
limit() method also accepts a function to dynamically calculate it. You can inject various utilities into the function as parameters.Export column
$column
Filament\Actions\Exports\ExportColumn
The current export column instance.
Exporter
$exporter
?Filament\Actions\Exports\Exporter
The instance of the exporter class that is currently being used for exporting data.
Options
$options
array<string, mixed>
The options that were defined when the export started.
Eloquent record
$record
?Illuminate\Database\Eloquent\Model
The Eloquent record that is currently being exported.
Limiting word count
You may limit the number ofwords() displayed in the cell:
As well as allowing a static value, the words() method also accepts a function to dynamically calculate it. You can inject various utilities into the function as parameters.
words() method also accepts a function to dynamically calculate it. You can inject various utilities into the function as parameters.Export column
$column
Filament\Actions\Exports\ExportColumn
The current export column instance.
Exporter
$exporter
?Filament\Actions\Exports\Exporter
The instance of the exporter class that is currently being used for exporting data.
Options
$options
array<string, mixed>
The options that were defined when the export started.
Eloquent record
$record
?Illuminate\Database\Eloquent\Model
The Eloquent record that is currently being exported.
Adding a prefix or suffix
You may add aprefix() or suffix() to the cell’s value:
As well as allowing static values, the prefix() and suffix() methods also accept functions to dynamically calculate them. You can inject various utilities into the functions as parameters.
prefix() and suffix() methods also accept functions to dynamically calculate them. You can inject various utilities into the functions as parameters.Export column
$column
Filament\Actions\Exports\ExportColumn
The current export column instance.
Exporter
$exporter
?Filament\Actions\Exports\Exporter
The instance of the exporter class that is currently being used for exporting data.
Options
$options
array<string, mixed>
The options that were defined when the export started.
Eloquent record
$record
?Illuminate\Database\Eloquent\Model
The Eloquent record that is currently being exported.
Exporting multiple values in a cell
By default, if there are multiple values in the column, they will be comma-separated. You may use thelistAsJson() method to list them as a JSON array instead:
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:Counting relationships
If you wish to count the number of related records in a column, you may use thecounts() method:
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 counting, 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:
Determining relationship existence
If you simply wish to indicate whether related records exist in a column, you may use theexists() method:
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 checking existence, 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:
Aggregating relationships
Filament provides several methods for aggregating a relationship field, includingavg(), 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:
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 aggregating, 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:
Configuring the export formats
By default, the export action will generate both CSV and XLSX formats and allow user to choose between them in the notification. You can use theExportFormat enum to customize this, by passing an array of formats to the formats() method on the action:
getFormats() method on the exporter class, which will set the default formats for all actions that use that exporter:
Modifying the export query
By default, if you are using theExportAction 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:
$options argument into the function, which is an array of options for that export:
modifyQuery() method on the exporter class, which will modify the query for all actions that use that exporter:
Configuring the export filesystem
Customizing the storage disk
By default, exported files will be uploaded to the storage disk defined in the configuration file, which ispublic by default. You can set the FILESYSTEM_DISK environment variable to change this.
While using the public disk a good default for many parts of Filament, using it for exports would result in exported files being stored in a public location. As such, if the default filesystem disk is public and a local disk exists in your config/filesystems.php, Filament will use the local disk for exports instead. If you override the disk to be public for an ExportAction or inside an exporter class, Filament will use that.
In production, you should use a disk such as s3 with a private access policy, to prevent unauthorized access to the exported files.
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:
boot() method of a service provider such as AppServiceProvider:
getFileDisk() method on the exporter class, returning the name of the disk:
Configuring the export file names
By default, exported files are given a name generated based on the export’s ID and type. You can customize the file name by using thefileName() method on the action:
getFileName() method on the exporter class and return a custom string:
Using export options
The export action can render extra form components that the user can interact with when exporting a CSV. This can be useful to allow the user to customize the behavior of the exporter. For instance, you might want a user to be able to choose the format of specific columns when exporting. To do this, you can return options form components from thegetOptionsFormComponents() method on your exporter class:
options() method on the action:
As well as allowing a static value, the options() method also accepts a function to dynamically calculate it. You can inject various utilities into the function as parameters.
options() method also accepts a function to dynamically calculate it. You can inject various utilities into the function as parameters.Learn more about utility injection.
Action
$action
Filament\Actions\Action
The current action instance.
Arguments
$arguments
array<string, mixed>
The array of arguments passed to the action when it was triggered.
Data
$data
array<string, mixed>
The array of data submitted from form fields in the action's modal. It will be empty before the modal form is submitted.
Livewire
$livewire
Livewire\Component
The Livewire component instance.
Eloquent model FQN
$model
?string<Illuminate\Database\Eloquent\Model>
The Eloquent model FQN for the current action, if one is attached.
Mounted actions
$mountedActions
array<Filament\Actions\Action>
The array of actions that are currently mounted in the Livewire component. This is useful for accessing data from parent actions.
Eloquent record
$record
?Illuminate\Database\Eloquent\Model
The Eloquent record for the current action, if one is attached.
Schema
$schema
Filament\Schemas\Schema
[Actions in schemas only] The schema object that this action belongs to.
Schema component
$schemaComponent
Filament\Schemas\Components\Component
[Actions in schemas only] The schema component that this action belongs to.
Schema component state
$schemaComponentState
mixed
[Actions in schemas only] The current value of the schema component.
Schema get function
$schemaGet
Filament\Schemas\Components\Utilities\Get
[Actions in schemas only] A function for retrieving values from the schema data. Validation is not run on form fields.
Schema operation
$schemaOperation
string
[Actions in schemas only] The current operation being performed by the schema. Usually
create, edit, or view.Schema set function
$schemaSet
Filament\Schemas\Components\Utilities\Set
[Actions in schemas only] A function for setting values in the schema data.
Schema state
$schemaState
mixed
[Actions in schemas only] The current value of the schema that this action belongs to, like the current repeater item.
Selected Eloquent records
$selectedRecords
Illuminate\Support\Collection
[Bulk actions only] The Eloquent records selected in the table.
Table
$table
Filament\Tables\Table
[Actions in tables only] The table object that this action belongs to.
$options argument into any closure function. For example, you might want to use it inside formatStateUsing() to format a column’s value:
$options argument is passed to all closure functions, you can access it inside limit():
Using a custom user model
By default, theexports table has a user_id column. That column is constrained to the users table:
Export model, the user() relationship is defined as a BelongsTo relationship to the App\Models\User model. If the App\Models\User model does not exist, or you want to use a different one, you can bind a new Authenticatable model to the container in a service provider’s register() method:
users, you should pass that table name to constrained():
Using a polymorphic user relationship
If you want to associate exports with multiple user models, you can use a polymorphicMorphTo relationship instead. To do this, you need to replace the user_id column in the exports table:
boot() method, you should call Export::polymorphicUserRelationship() to swap the user() relationship on the Export model to a MorphTo relationship:
Limiting the maximum number of rows that can be exported
To prevent server overload, you may wish to limit the maximum number of rows that can be exported from one CSV file. You can do this by calling themaxRows() method on the action:
Changing the export chunk size
Filament will chunk the CSV, and process each chunk in a different queued job. By default, chunks are 100 rows at a time. You can change this by calling thechunkSize() method on the action:
As well as allowing a static value, the chunkSize() method also accepts a function to dynamically calculate it. You can inject various utilities into the function as parameters.
chunkSize() method also accepts a function to dynamically calculate it. You can inject various utilities into the function as parameters.Learn more about utility injection.
Action
$action
Filament\Actions\Action
The current action instance.
Arguments
$arguments
array<string, mixed>
The array of arguments passed to the action when it was triggered.
Data
$data
array<string, mixed>
The array of data submitted from form fields in the action's modal. It will be empty before the modal form is submitted.
Livewire
$livewire
Livewire\Component
The Livewire component instance.
Eloquent model FQN
$model
?string<Illuminate\Database\Eloquent\Model>
The Eloquent model FQN for the current action, if one is attached.
Mounted actions
$mountedActions
array<Filament\Actions\Action>
The array of actions that are currently mounted in the Livewire component. This is useful for accessing data from parent actions.
Eloquent record
$record
?Illuminate\Database\Eloquent\Model
The Eloquent record for the current action, if one is attached.
Schema
$schema
Filament\Schemas\Schema
[Actions in schemas only] The schema object that this action belongs to.
Schema component
$schemaComponent
Filament\Schemas\Components\Component
[Actions in schemas only] The schema component that this action belongs to.
Schema component state
$schemaComponentState
mixed
[Actions in schemas only] The current value of the schema component.
Schema get function
$schemaGet
Filament\Schemas\Components\Utilities\Get
[Actions in schemas only] A function for retrieving values from the schema data. Validation is not run on form fields.
Schema operation
$schemaOperation
string
[Actions in schemas only] The current operation being performed by the schema. Usually
create, edit, or view.Schema set function
$schemaSet
Filament\Schemas\Components\Utilities\Set
[Actions in schemas only] A function for setting values in the schema data.
Schema state
$schemaState
mixed
[Actions in schemas only] The current value of the schema that this action belongs to, like the current repeater item.
Selected Eloquent records
$selectedRecords
Illuminate\Support\Collection
[Bulk actions only] The Eloquent records selected in the table.
Table
$table
Filament\Tables\Table
[Actions in tables only] The table object that this action belongs to.
Changing the CSV delimiter
The default delimiter for CSVs is the comma (,). If you want to export using a different delimiter, you may override the getCsvDelimiter() method on the exporter class, returning a new one:
As well as allowing a static value, the csvDelimiter() method also accepts a function to dynamically calculate it. You can inject various utilities into the function as parameters.
csvDelimiter() method also accepts a function to dynamically calculate it. You can inject various utilities into the function as parameters.Learn more about utility injection.
Action
$action
Filament\Actions\Action
The current action instance.
Arguments
$arguments
array<string, mixed>
The array of arguments passed to the action when it was triggered.
Data
$data
array<string, mixed>
The array of data submitted from form fields in the action's modal. It will be empty before the modal form is submitted.
Livewire
$livewire
Livewire\Component
The Livewire component instance.
Eloquent model FQN
$model
?string<Illuminate\Database\Eloquent\Model>
The Eloquent model FQN for the current action, if one is attached.
Mounted actions
$mountedActions
array<Filament\Actions\Action>
The array of actions that are currently mounted in the Livewire component. This is useful for accessing data from parent actions.
Eloquent record
$record
?Illuminate\Database\Eloquent\Model
The Eloquent record for the current action, if one is attached.
Schema
$schema
Filament\Schemas\Schema
[Actions in schemas only] The schema object that this action belongs to.
Schema component
$schemaComponent
Filament\Schemas\Components\Component
[Actions in schemas only] The schema component that this action belongs to.
Schema component state
$schemaComponentState
mixed
[Actions in schemas only] The current value of the schema component.
Schema get function
$schemaGet
Filament\Schemas\Components\Utilities\Get
[Actions in schemas only] A function for retrieving values from the schema data. Validation is not run on form fields.
Schema operation
$schemaOperation
string
[Actions in schemas only] The current operation being performed by the schema. Usually
create, edit, or view.Schema set function
$schemaSet
Filament\Schemas\Components\Utilities\Set
[Actions in schemas only] A function for setting values in the schema data.
Schema state
$schemaState
mixed
[Actions in schemas only] The current value of the schema that this action belongs to, like the current repeater item.
Selected Eloquent records
$selectedRecords
Illuminate\Support\Collection
[Bulk actions only] The Eloquent records selected in the table.
Table
$table
Filament\Tables\Table
[Actions in tables only] The table object that this action belongs to.
Customizing XLSX files
Styling XLSX rows
If you want to style the cells of the XLSX file, you may override thegetXlsxCellStyle() method on the exporter class, returning an OpenSpout Style object:
getXlsxHeaderCellStyle() method on the exporter class, returning an OpenSpout Style object:
Styling XLSX columns
ThemakeXlsxRow() and makeXlsxHeaderRow() methods on the exporter class allow you to customize the styling of individual cells within a row. By default, the methods are implemented like this:
$this->columnMap property may be used to determine which columns are being exported and in which order. You can replace Row::fromValues() with an array of Cell objects, which allow you to style them individually using OpenSpout Style objects. A StyleMerger can be used to merge the default style with the custom style for a cell, allowing you to apply additional styles on top of the default ones:
Customizing the XLSX writer
If you want to pass “options” to the OpenSpout XLSXWriter, you can return an OpenSpout\Writer\XLSX\Options instance from the getXlsxWriterOptions() method of the exporter class:
configureXlsxWriterBeforeClosing() method on the exporter class. This method receives the Writer instance as a parameter, and you can modify it before it is closed:
Customizing the export job
The default job for processing exports isFilament\Actions\Exports\Jobs\PrepareCsvExport. If you want to extend this class and override any of its methods, you may replace the original class in the register() method of a service provider:
job() method on the action, to customize the job for a specific export:
Customizing the export queue and connection
By default, the export system will use the default queue and connection. If you’d like to customize the queue used for jobs of a certain exporter, you may override thegetJobQueue() method in your exporter class:
getJobConnection() method in your exporter class:
Customizing the export job middleware
By default, the export system will only process one job at a time from each export. This is to prevent the server from being overloaded, and other jobs from being delayed by large exports. That functionality is defined in theWithoutOverlapping middleware on the exporter class:
Customizing the export job retries
By default, the export system will retry a job for 24 hours, or until it fails with 5 unhandled exceptions, whichever happens first. This is to allow for temporary issues, such as the database being unavailable, to be resolved. You may change the time period for the job to retry, which is defined in thegetJobRetryUntil() method on the exporter class:
Customizing the export job backoff strategy
By default, the export system will wait 1 minute, then 2 minutes, then 5 minutes, then 10 minutes thereafter before retrying a job. This is to prevent the server from being overloaded by a job that is failing repeatedly. That functionality is defined in thegetJobBackoff() method on the exporter class:
Customizing the export job tags
By default, the export system will tag each job with the ID of the export. This is to allow you to easily find all jobs related to a certain export. That functionality is defined in thegetJobTags() method on the exporter class:
Customizing the export job batch name
By default, the export system doesn’t define any name for the job batches. If you’d like to customize the name that is applied to job batches of a certain exporter, you may override thegetJobBatchName() method in your exporter class:
Authorization
By default, only the user who started the export may download files that get generated. If you’d like to customize the authorization logic, you may create anExportPolicy class, and register it in your AuthServiceProvider:
view() method of the policy will be used to authorize access to the downloads.
Please note that if you define a policy, the existing logic of ensuring only the user who started the export can access it will be removed. You will need to add that logic to your policy if you want to keep it: