Action

Export action

简介

Filament 包含一个可以将行数据导出到 CSV 或者 XLSX 文件的 Action。点击触发按钮后,会打开一个模态框询问用户要导出那些列,以及这些类要使用什么标签。该特性使用了队列批量操作数据库通知,因此你需要发布这些迁移到 Laravel 中。同时,也需要发布 Filament 用于存储导出数据的迁移表:

php artisan make:queue-batches-table
php artisan make:notifications-table
php artisan vendor:publish --tag=filament-actions-migrations
php artisan migrate

If you’d like to receive export notifications in a panel, you can enable them in the panel configuration.

NOTE

如果你使用的是 PostgreSQL,请确保通知迁移中的 data 字段使用 json(): $table->json('data')

NOTE

如果 User 模型使用了 UUID,请确保通知迁移的 notifiable 字段使用 uuidMorphs(): $table->uuidMorphs('notifiable')

ExportAction 可以这样使用:

use App\Filament\Exports\ProductExporter;
use Filament\Actions\ExportAction;

ExportAction::make()
    ->exporter(ProductExporter::class)
Export action modal

如果你想将该 Action 添加到表头,可以这样使用:

use App\Filament\Exports\ProductExporter;
use Filament\Actions\ExportAction;
use Filament\Tables\Table;

public function table(Table $table): Table
{
    return $table
        ->headerActions([
            ExportAction::make()
                ->exporter(ProductExporter::class),
        ]);
}

或者,如果你想将其添加为表格的批量操作,让用户可以选择导出哪些行,可以使用 Filament\Actions\ExportBulkAction

use App\Filament\Exports\ProductExporter;
use Filament\Actions\ExportBulkAction;
use Filament\Tables\Table;

public function table(Table $table): Table
{
    return $table
        ->toolbarActions([
            ExportBulkAction::make()
                ->exporter(ProductExporter::class),
        ]);
}

此处需要创建 “exporter” 类,以告知 Filament 如何导出每一行。

创建导出器

要为模型创建 exporter 类,可以使用 make:filament-exporter 命令,并传入模型名称:

php artisan make:filament-exporter Product

该命令将在 app/Filament/Exports 目录下创建一个新类。你需要定义可被导出的列字段

自动生成导出列

如果你想节省时间,可以使用 --generate,Filament 将基于模型的数据库字段,为你自动生成列字段

php artisan make:filament-exporter Product --generate

定义导出字段

要定义可被导出的列,你需要在导出器类中重写 getColumns() 方法,并使其返回 ExportColumn 对象数组:

use Filament\Actions\Exports\ExportColumn;

public static function getColumns(): array
{
    return [
        ExportColumn::make('name'),
        ExportColumn::make('sku')
            ->label('SKU'),
        ExportColumn::make('price'),
    ];
}

自定义导出列标签

每个列的标签将由该列名称自动生成,不过你也可以调用 label() 方法对其进行重写:

use Filament\Actions\Exports\ExportColumn;

ExportColumn::make('sku')
    ->label('SKU')

配置默认选择的列

默认情况下,当用户被询问要导出哪些列时,所有的列都会被选中。你可以使用 enabledByDefault() 方法自定义默认选择的列:

use Filament\Actions\Exports\ExportColumn;

ExportColumn::make('description')
    ->enabledByDefault(false)

You can use the 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:

use App\Filament\Exports\ProductExporter;
use Filament\Actions\ExportAction;

ExportAction::make()
    ->exporter(ProductExporter::class)
    ->enableVisibleTableColumnsByDefault()

Hiding an export column

You may hide a column entirely by using the hidden() or visible() method. A hidden column is not shown in the column selection form, and is never written to the exported file:

use Filament\Actions\Exports\ExportColumn;

ExportColumn::make('sku')
    ->hidden()

ExportColumn::make('sku')
    ->visible()

To hide a column conditionally, you may pass a boolean value to either method:

use Filament\Actions\Exports\ExportColumn;

ExportColumn::make('cost_price')
    ->hidden(fn (): bool => ! auth()->user()->isAdmin())

ExportColumn::make('cost_price')
    ->visible(fn (): bool => auth()->user()->isAdmin())

NOTE

Unlike table columns, export columns are resolved without a record, so a hidden() or visible() closure cannot depend on row data. Use it for schema-level conditions such as the authenticated user, feature flags, or configuration.

To keep a column selectable but unchecked by default instead of hiding it entirely, use enabledByDefault(false).

Configuring the column selection form layout

By default, the column selection form uses a single column layout. You can change this using the columnMappingColumns() method, passing the number of columns you would like to use for the layout on large screens:

use App\Filament\Exports\ProductExporter;
use Filament\Actions\ExportAction;

ExportAction::make()
    ->exporter(ProductExporter::class)
    ->columnMappingColumns(3)

This will display the column selection checkboxes and label inputs in a 3-column layout, making better use of available space when you have many exportable columns. Note that while there will be three columns in the layout on large screens, the layout will still be responsive and there will be fewer columns displayed on smaller screens.

禁用列选择

默认情况下,会询问用户要导出哪些列。你可以使用 columnMapping(false) 方法禁用该功能。

use App\Filament\Exports\ProductExporter;
use Filament\Actions\ExportAction;

ExportAction::make()
    ->exporter(ProductExporter::class)
    ->columnMapping(false)

计算导出列状态

有时,你需要计算列状态,而不是直接从数据库字段中读取。

传入一个回调函数到 state() 方法,你可以自定义该列基于 $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);
    })
除了 $record 之外,state() 函数可以注入各种 utility 作为参数。
Utility 类型 参数 描述
Export column Filament\Actions\Exports\ExportColumn $column The current export column instance.
Exporter ?Filament\Actions\Exports\Exporter $exporter The instance of the exporter class that is currently being used for exporting data.
Options array<string, mixed> $options The options that were defined when the export started.
Eloquent record ?Illuminate\Database\Eloquent\Model $record The Eloquent record that is currently being exported.

格式化导出列的值

你也可以传入一个自定义格式化回调函数给 formatStateUsing(),它接受单元格的 $state 和 Eloquent 记录 $record(可选):

use Filament\Actions\Exports\ExportColumn;

ExportColumn::make('status')
    ->formatStateUsing(fn (string $state): string => __("statuses.{$state}"))
除了 $state 之外,formatStateUsing() 函数可以注入各种 utility 作为参数。
Utility 类型 参数 描述
Export column Filament\Actions\Exports\ExportColumn $column The current export column instance.
Exporter ?Filament\Actions\Exports\Exporter $exporter The instance of the exporter class that is currently being used for exporting data.
Options array<string, mixed> $options The options that were defined when the export started.
Eloquent record ?Illuminate\Database\Eloquent\Model $record The Eloquent record that is currently being exported.
State mixed $state The state to format.

如果列中有多个值,每个值都会调用该函数。

限制文本长度

使用 limit() 方法,你可以限制单元格中的值的长度:

use Filament\Actions\Exports\ExportColumn;

ExportColumn::make('description')
    ->limit(50)
除了允许静态值之外,limit() 方法也可以接受函数动态计算其值。你可以将各种 utility 作为参数注入到函数中。
Utility 类型 参数 描述
Export column Filament\Actions\Exports\ExportColumn $column The current export column instance.
Exporter ?Filament\Actions\Exports\Exporter $exporter The instance of the exporter class that is currently being used for exporting data.
Options array<string, mixed> $options The options that were defined when the export started.
Eloquent record ?Illuminate\Database\Eloquent\Model $record The Eloquent record that is currently being exported.

限制字数

使用 words() 方法,你可以限制单元格中显示的字数:

use Filament\Actions\Exports\ExportColumn;

ExportColumn::make('description')
    ->words(10)
除了允许静态值之外,words() 方法也可以接受函数动态计算其值。你可以将各种 utility 作为参数注入到函数中。
Utility 类型 参数 描述
Export column Filament\Actions\Exports\ExportColumn $column The current export column instance.
Exporter ?Filament\Actions\Exports\Exporter $exporter The instance of the exporter class that is currently being used for exporting data.
Options array<string, mixed> $options The options that were defined when the export started.
Eloquent record ?Illuminate\Database\Eloquent\Model $record The Eloquent record that is currently being exported.

添加前后缀

使用 prefix() 或者 suffix(),你可以添加前缀或者后缀到单元格值中:

use Filament\Actions\Exports\ExportColumn;

ExportColumn::make('domain')
    ->prefix('https://')
    ->suffix('.com')
除了允许静态值之外,prefix()suffix() 方法也可以接受函数动态计算它们值。你可以将各种 utility 作为参数注入到函数中。
Utility 类型 参数 描述
Export column Filament\Actions\Exports\ExportColumn $column The current export column instance.
Exporter ?Filament\Actions\Exports\Exporter $exporter The instance of the exporter class that is currently being used for exporting data.
Options array<string, mixed> $options The options that were defined when the export started.
Eloquent record ?Illuminate\Database\Eloquent\Model $record The Eloquent record that is currently being exported.

在单元格中导出多个值

默认情况下,如果列中有多个值,将会使用逗号进行分隔。你可以使用 listAsJson() 方法将它们显示成 JSON 数组:

use Filament\Actions\Exports\ExportColumn;

ExportColumn::make('tags')
    ->listAsJson()

展示关联数据

你可以使用“点语法”访问关联中的列。首先是关联名,其后紧跟着点号,最后是要展示的列名:

use Filament\Actions\Exports\ExportColumn;

ExportColumn::make('author.name')

计数关联

如果你想计算列中的关联记录数量,可以使用 counts() 方法:

use Filament\Actions\Exports\ExportColumn;

ExportColumn::make('users_count')
    ->counts('users')

本例中,users 是要计算的关联名。列名必须为 users_count,因为这是 Laravel 用来存储计数结果 的命名规范。

如果你想在计算前设置关联的查询范围,你可以传入一个数组到该方法中,其中键是关联名,值为设置 Eloquent 查询范围的函数:

use Filament\Actions\Exports\ExportColumn;
use Illuminate\Database\Eloquent\Builder;

ExportColumn::make('users_count')
    ->counts([
        'users' => fn (Builder $query) => $query->where('is_active', true),
    ])

确定关联是否存在

如果你只是想说明列中是否存在关联记录,你可以使用 exists() 方法:

use Filament\Actions\Exports\ExportColumn;

ExportColumn::make('users_exists')
    ->exists('users')

本例中,users 是要检查是否存在的关联名。列名必须是 users_exists,因为这是 Laravel 用来存储计数结果 的命名规范。

如果你想在计算前设置关联的查询范围,你可以传入一个数组到该方法中,其中键是关联名,值为设置 Eloquent 查询范围的函数:

use Filament\Actions\Exports\ExportColumn;
use Illuminate\Database\Eloquent\Builder;

ExportColumn::make('users_exists')
    ->exists([
        'users' => fn (Builder $query) => $query->where('is_active', true),
    ])

聚合关联

Filament 提供多种方法用于聚合关联字段,包括 avg()max()min()sum()。比如,如果你想显示列中所有关联的平均值,你可以使用 avg() 方法:

use Filament\Actions\Exports\ExportColumn;

ExportColumn::make('users_avg_age')
    ->avg('users', 'age')

上例中,users 是关联名,而 age 是要计算平均值的字段。列名必须是 users_avg_age,因为这是 Laravel 用来存储聚合结果 的命名规范。

如果你想在计算前设置关联的查询范围,你可以传入一个数组到该方法中,其中键是关联名,值为设置 Eloquent 查询范围的函数:

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')

配置导出格式

默认情况下,导出 Action 会允许用户在 CSV 和 XLSX 格式之间做出选择。你可以在 Action 的 formats() 方法中传入一个格式数组,使用 ExportFormat 枚举对此进行自定义:

use App\Filament\Exports\ProductExporter;
use Filament\Actions\ExportAction;
use Filament\Actions\Exports\Enums\ExportFormat;

ExportAction::make()
    ->exporter(ProductExporter::class)
    ->formats([
        ExportFormat::Csv,
    ])
    // or
    ->formats([
        ExportFormat::Xlsx,
    ])
    // or
    ->formats([
        ExportFormat::Xlsx,
        ExportFormat::Csv,
    ])

此外,你可以在导出器类中重写 getFormats() 方法,为所有使用该导出器的 Action 设置默认格式:

use Filament\Actions\Exports\Enums\ExportFormat;

public function getFormats(): array
{
    return [
        ExportFormat::Csv,
    ];
}

Customizing how export files are downloaded

By default, each export format uses its own downloader to return a streamed response. You may customize the downloader for an exporter by overriding the getDownloader() method:

use App\Filament\Exports\Downloaders\CustomCsvDownloader;
use App\Filament\Exports\Downloaders\CustomXlsxDownloader;
use Filament\Actions\Exports\Downloaders\Contracts\Downloader;
use Filament\Actions\Exports\Enums\Contracts\ExportFormat as ExportFormatInterface;
use Filament\Actions\Exports\Enums\ExportFormat;

public static function getDownloader(ExportFormatInterface $format): Downloader
{
    return match ($format) {
        ExportFormat::Csv => app(CustomCsvDownloader::class),
        ExportFormat::Xlsx => app(CustomXlsxDownloader::class),
        default => $format->getDownloader(),
    };
}

A downloader is an invokable class that accepts the Export model and returns a Symfony Response. This response may stream a download, return a file, or redirect the user to a temporary URL on a remote filesystem.

Filament’s built-in download route only resolves the ExportFormat::Csv and ExportFormat::Xlsx formats. If you use a custom ExportFormatInterface implementation, its getDownloadNotificationAction() method must link to a route that handles the custom format.

If your custom downloader only changes how the generated content is delivered, you may use CsvExportContentGenerator to iterate over the generated CSV chunks, or XlsxExportContentGenerator to write the generated rows to an OpenSpout Writer. Filament resolves both classes from the container so that you can reuse the built-in content generation without duplicating it. You must open the Writer before passing it to XlsxExportContentGenerator and close it afterwards. XlsxExportContentGenerator mirrors Filament’s on-demand XLSX download and does not apply the writer options, styles, custom row creation, or writer lifecycle hooks that are used when the queued XLSX file is generated.

修改导出查询

默认情况下,如果在表格中使用 ExportAction,该操作竟会使用表格当前的过滤和排序查询来导出数据。如果你没有表格,它将使用模型的默认查询。要在导出前修改查询构造器,你可以在该操作上使用 modifyQueryUsing() 方法:

use App\Filament\Exports\ProductExporter;
use Filament\Actions\ExportAction;
use Illuminate\Database\Eloquent\Builder;

ExportAction::make()
    ->exporter(ProductExporter::class)
    ->modifyQueryUsing(fn (Builder $query) => $query->where('is_active', true))

你也可以注入 $options 参数到该函数中,该参数是用以导出的选项数组:

use App\Filament\Exports\ProductExporter;
use Illuminate\Database\Eloquent\Builder;

ExportAction::make()
    ->exporter(ProductExporter::class)
    ->modifyQueryUsing(fn (Builder $query, array $options) => $query->where('is_active', $options['isActive'] ?? true))

此外,你可以在导出器类中重写 modifyQuery() 方法,它将修改使用该导出器的所有 Action 的查询:

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'],
        ]),
    ]);
}

配置导出文件系统

自定义存储磁盘

默认情况下,导出文件会上传到配置文件中定义的存储磁盘中(默认为 public)。你也可以设置 FILESYSTEM_DISK 环境变量来修改他它。

虽然使用 public 磁盘是 Filament 许多包的默认设置,但将其用于导出会导致导出的文件存储在公共位置。因此,如果默认的文件系统磁盘是public,并且你的 config/filesystems.php 中存在 local 磁盘,Filament 将使用 local 盘进行导出。如果你为 ExportAction 或导出器类将磁盘重写为 public,Filament 将使用它。

在生产环境中,你应该使用具有私有访问策略的磁盘,如 s3,以防止对导出文件的未经授权的访问。

如果你想为特定的导出使用不同的磁盘,你可以将磁盘名称传递给操作上的 disk() 方法:

use Filament\Actions\ExportAction;

ExportAction::make()
    ->exporter(ProductExporter::class)
    ->fileDisk('s3')

你可以在服务提供者(比如 AppServiceProvider)的 boot() 方法中一次性设置所有导出 Action 的磁盘:

use Filament\Actions\ExportAction;

ExportAction::configureUsing(fn (ExportAction $action) => $action->fileDisk('s3'));

此外,你也可以重写导出器类的 getFileDisk(),使之返回磁盘名:

public function getFileDisk(): string
{
    return 's3';
}

在有需要时删除创建的导出文件是开发者的责任,Filament 不会删除这些文件,以防以后需要再次下载导出。

配置导出文件名

默认情况下,导出文件的名称是基于 ID 和导出类型生成的。你可以使用 Action 上的 fileName() 方法,自定义文件名:

use Filament\Actions\ExportAction;
use Filament\Actions\Exports\Models\Export;

ExportAction::make()
    ->exporter(ProductExporter::class)
    ->fileName(fn (Export $export): string => "products-{$export->getKey()}")

此外,你也可以重写导出器类的 getFileName(),使之返回自定义字符串:

use Filament\Actions\Exports\Models\Export;

public function getFileName(Export $export): string
{
    return "products-{$export->getKey()}";
}

使用导出选项

导出操作可以渲染用户在导出 CSV 时可以与之交互的额外表单组件。这对于允许用户自定义导出器的行为非常有用。例如,你可能希望用户在导出时能够选择特定列的格式。为此,你可以在导出器类上的 getOptionsFormComponent() 方法返回选项表单组件:

use Filament\Forms\Components\TextInput;

public static function getOptionsFormComponents(): array
{
    return [
        TextInput::make('descriptionLimit')
            ->label('Limit the length of the description column content')
            ->integer(),
    ];
}

此外,你也可以通过 Action 的 option() 方法传递一组静态选项给导出器:

use App\Filament\Exports\ProductExporter;
use Filament\Actions\ExportAction;

ExportAction::make()
    ->exporter(ProductExporter::class)
    ->options([
        'descriptionLimit' => 250,
    ])
除了允许静态值之外,options() 方法也可以接受函数动态计算其值。你可以将各种 utility 作为参数注入到函数中。 了解更多 utility 注入详情。
Utility 类型 参数 描述
Action Filament\Actions\Action $action The current action instance.
Arguments array<string, mixed> $arguments The array of arguments passed to the action when it was triggered.
Data array<string, mixed> $data 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\Component $livewire The Livewire component instance.
Eloquent model FQN ?string<Illuminate\Database\Eloquent\Model> $model The Eloquent model FQN for the current action, if one is attached.
Mounted actions array<Filament\Actions\Action> $mountedActions The array of actions that are currently mounted in the Livewire component. This is useful for accessing data from parent actions.
Eloquent record ?Illuminate\Database\Eloquent\Model $record The Eloquent record for the current action, if one is attached.
Schema Filament\Schemas\Schema $schema [Actions in schemas only] The schema object that this action belongs to.
Schema component Filament\Schemas\Components\Component $schemaComponent [Actions in schemas only] The schema component that this action belongs to.
Schema component state mixed $schemaComponentState [Actions in schemas only] The current value of the schema component.
Schema get function Filament\Schemas\Components\Utilities\Get $schemaGet [Actions in schemas only] A function for retrieving values from the schema data. Validation is not run on form fields.
Schema operation string $schemaOperation [Actions in schemas only] The current operation being performed by the schema. Usually create, edit, or view.
Schema set function Filament\Schemas\Components\Utilities\Set $schemaSet [Actions in schemas only] A function for setting values in the schema data.
Selected Eloquent records Illuminate\Support\Collection $selectedRecords [Bulk actions only] The Eloquent records selected in the table.
Table Filament\Tables\Table $table [Actions in tables only] The table object that this action belongs to.

现在,通过将 $options 参数注入到闭包函数中,你可以在导出类中访问这些选项的数据。比如,如果你想在 formatStateUsing() 中使用它们来格式化列值

use Filament\Actions\Exports\ExportColumn;

ExportColumn::make('description')
    ->formatStateUsing(function (string $state, array $options): string {
        return (string) str($state)->limit($options['descriptionLimit'] ?? 100);
    })

此外,因为 $options 参数被传入到所有闭包函数中,你可以在 limit() 中访问:

use Filament\Actions\Exports\ExportColumn;

ExportColumn::make('description')
    ->limit(fn (array $options): int => $options['descriptionLimit'] ?? 100)

使用自定义用户模型

默认情况下,exports 表有一个 user_id 列。该列被约束到 users 表:

$table->foreignId('user_id')->constrained()->cascadeOnDelete();

Export 模型中,user() 关联被定义指向 App\Models\User 模型的 BelongsTo 关联。如果 App\Models\User 模型不存在,或者你想使用其他模型,你可以在服务提供者的 register() 方法中将新的 Authenticatable 模型绑定到容器中:

use App\Models\Admin;
use Illuminate\Contracts\Auth\Authenticatable;

$this->app->bind(Authenticatable::class, Admin::class);

如果你的 authenticatable 模型使用了不同于 users 的表,你应该将其表名传入到 constrained() 中:

$table->foreignId('user_id')->constrained('admins')->cascadeOnDelete();

使用多态用户关联

如果你希望导航关联多个用户模型,你可以使用多态 MorphTo 关联。为此,你需要在 exports 表中替换 user_id 列:

$table->morphs('user');

然后,在服务提供者的 boot() 方法中,你应该调用 Export::polymorphicUserRelationship(),将 Export 模型上的 user 关联修改为 MorphTo 关联:

use Filament\Actions\Exports\Models\Export;

Export::polymorphicUserRelationship();

限制可导出的最大行数

要防止服务器过载,你可能会希望限制一个 CSV 文件导出的最大行数。为此,你可以在 Action 上调用 maxRows() 方法:

use App\Filament\Exports\ProductExporter;
use Filament\Actions\ExportAction;

ExportAction::make()
    ->exporter(ProductExporter::class)
    ->maxRows(100000)

修改导出分块大小

Filament 将对 CSV 进行分块(chunk),并在不同的队列作业中处理每个分块。默认情况下,每个分块是 100 行。你可以调用 Action 上的 chunkSize() 方法来修改:

use App\Filament\Exports\ProductExporter;
use Filament\Actions\ExportAction;

ExportAction::make()
    ->exporter(ProductExporter::class)
    ->chunkSize(250)
除了允许静态值之外,chunkSize() 方法也可以接受函数动态计算其值。你可以将各种 utility 作为参数注入到函数中。 了解更多 utility 注入详情。
Utility 类型 参数 描述
Action Filament\Actions\Action $action The current action instance.
Arguments array<string, mixed> $arguments The array of arguments passed to the action when it was triggered.
Data array<string, mixed> $data 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\Component $livewire The Livewire component instance.
Eloquent model FQN ?string<Illuminate\Database\Eloquent\Model> $model The Eloquent model FQN for the current action, if one is attached.
Mounted actions array<Filament\Actions\Action> $mountedActions The array of actions that are currently mounted in the Livewire component. This is useful for accessing data from parent actions.
Eloquent record ?Illuminate\Database\Eloquent\Model $record The Eloquent record for the current action, if one is attached.
Schema Filament\Schemas\Schema $schema [Actions in schemas only] The schema object that this action belongs to.
Schema component Filament\Schemas\Components\Component $schemaComponent [Actions in schemas only] The schema component that this action belongs to.
Schema component state mixed $schemaComponentState [Actions in schemas only] The current value of the schema component.
Schema get function Filament\Schemas\Components\Utilities\Get $schemaGet [Actions in schemas only] A function for retrieving values from the schema data. Validation is not run on form fields.
Schema operation string $schemaOperation [Actions in schemas only] The current operation being performed by the schema. Usually create, edit, or view.
Schema set function Filament\Schemas\Components\Utilities\Set $schemaSet [Actions in schemas only] A function for setting values in the schema data.
Selected Eloquent records Illuminate\Support\Collection $selectedRecords [Bulk actions only] The Eloquent records selected in the table.
Table Filament\Tables\Table $table [Actions in tables only] The table object that this action belongs to.

TIP

如果你在导入大型 CSV 文件时遇到内存问题或者出现超时,缩减分块大小或许就是一个不错的方案。

修改 CSV 分隔符

默认的 CSV 分隔符是逗号(,)。如果你想要其他分隔符进行导出,你可以重写该 Exporter 类上的 getCsvDelimiter() 方法,并返回新的分隔符:

public static function getCsvDelimiter(): string
{
    return ';';
}
除了允许静态值之外,csvDelimiter() 方法也可以接受函数动态计算其值。你可以将各种 utility 作为参数注入到函数中。 了解更多 utility 注入详情。
Utility 类型 参数 描述
Action Filament\Actions\Action $action The current action instance.
Arguments array<string, mixed> $arguments The array of arguments passed to the action when it was triggered.
Data array<string, mixed> $data 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\Component $livewire The Livewire component instance.
Eloquent model FQN ?string<Illuminate\Database\Eloquent\Model> $model The Eloquent model FQN for the current action, if one is attached.
Mounted actions array<Filament\Actions\Action> $mountedActions The array of actions that are currently mounted in the Livewire component. This is useful for accessing data from parent actions.
Eloquent record ?Illuminate\Database\Eloquent\Model $record The Eloquent record for the current action, if one is attached.
Schema Filament\Schemas\Schema $schema [Actions in schemas only] The schema object that this action belongs to.
Schema component Filament\Schemas\Components\Component $schemaComponent [Actions in schemas only] The schema component that this action belongs to.
Schema component state mixed $schemaComponentState [Actions in schemas only] The current value of the schema component.
Schema get function Filament\Schemas\Components\Utilities\Get $schemaGet [Actions in schemas only] A function for retrieving values from the schema data. Validation is not run on form fields.
Schema operation string $schemaOperation [Actions in schemas only] The current operation being performed by the schema. Usually create, edit, or view.
Schema set function Filament\Schemas\Components\Utilities\Set $schemaSet [Actions in schemas only] A function for setting values in the schema data.
Selected Eloquent records Illuminate\Support\Collection $selectedRecords [Bulk actions only] The Eloquent records selected in the table.
Table Filament\Tables\Table $table [Actions in tables only] The table object that this action belongs to.

你只能指定单个字符,否则会抛出异常。

自定义 XLSX 文件

XLSX 行样式

如果你想设置 XLSX 文件的单元格样式,你可以重写导出器类的 getXlsxCellStyle(),使之返回 OpenSpout Style 对象

use OpenSpout\Common\Entity\Style\Style;

public function getXlsxCellStyle(): ?Style
{
    return (new Style())
        ->setFontSize(12)
        ->setFontName('Consolas');
}

如果你只想修改 XLSX 文件的标题单元格样式,你可以重写导出器类的 getXlsxHeaderCellStyle(),使之返回 OpenSpout Style 对象

use OpenSpout\Common\Entity\Style\CellAlignment;
use OpenSpout\Common\Entity\Style\CellVerticalAlignment;
use OpenSpout\Common\Entity\Style\Color;
use OpenSpout\Common\Entity\Style\Style;

public function getXlsxHeaderCellStyle(): ?Style
{
    return (new Style())
        ->setFontBold()
        ->setFontItalic()
        ->setFontSize(14)
        ->setFontName('Consolas')
        ->setFontColor(Color::rgb(255, 255, 77))
        ->setBackgroundColor(Color::rgb(0, 0, 0))
        ->setCellAlignment(CellAlignment::CENTER)
        ->setCellVerticalAlignment(CellVerticalAlignment::CENTER);
}

XLSX 列样式

导出器类上的 makeXlsxRow()makeXlsxHeaderRow() 方法允许你在某一行内自定义单独的单元格样式。默认情况下,该方法的实现如下:

use OpenSpout\Common\Entity\Row;
use OpenSpout\Common\Entity\Style\Style;

/**
 * @param array<mixed> $values
 */
public function makeXlsxRow(array $values, ?Style $style = null): Row
{
    return Row::fromValues($values, $style);
}

用户导出时,他们可以选择导出哪些行。因此,$this->columnMap 属性可以用于确定哪些行以何种顺序导出。你可以将 Row::fromValues() 替换成一个 Cell 对象数组,以允许你使用 OpenSpout Style 对象单独设置样式。StyleMerger 可以用于将单元格的自定义样式与默认样式进行合并,允许你在默认样式基础上应用其他样式:

use OpenSpout\Common\Entity\Cell;
use OpenSpout\Common\Entity\Row;
use OpenSpout\Common\Entity\Style\Style;
use OpenSpout\Writer\Common\Manager\Style\StyleMerger;

/**
 * @param array<mixed> $values
 */
public function makeXlsxRow(array $values, ?Style $style = null): Row
{
    $styleMerger = new StyleMerger();

    $cells = [];
    
    foreach (array_keys($this->columnMap) as $columnIndex => $column) {
        $cells[] = match ($column) {
            'name' => Cell::fromValue(
                $values[$columnIndex],
                $styleMerger->merge(
                    (new Style())->setFontUnderline(),
                    $style,
                ),
            ),
            'price' => Cell::fromValue(
                $values[$columnIndex],
                (new Style())->setFontSize(12),
            ),
            default => Cell::fromValue($values[$columnIndex]),
        },
    }
    
    return new Row($cells, $style);
}

自定义 XLSX writer

如果你想要传递选项给 OpenSpout XLSX Writer,你可以在导出器类的 getXlsxWriterOptions() 方法中返回 OpenSpout\Writer\XLSX\Options 实例:

use OpenSpout\Writer\XLSX\Options;

public function getXlsxWriterOptions(): ?Options
{
    $options = new Options();
    $options->setColumnWidth(10, 1);
    $options->setColumnWidthForRange(12, 2, 3);
    
    return $options;
}

如果你想在其打开后自定义 XLSX writer,你可以在导出器类上重写 configureXlsxWriterAfterOpen()。该方法接收 Writer 实例作为参数,你可以在写入表头和行数据之前修改它。这对于在导出表格上添加额外的行(比如标题,子标题)非常有用:

use OpenSpout\Common\Entity\Row;
use OpenSpout\Common\Entity\Style\Style;
use OpenSpout\Writer\XLSX\Writer;

public function configureXlsxWriterAfterOpen(Writer $writer): Writer
{
    $writer->addRow(Row::fromValues(
        ['This is a custom header added after opening the XLSX writer.'],
        (new Style())->setShouldWrapText(false),
    ));

    return $writer;
}

NOTE

Any rows you add here appear above the header row, shifting the exported table down. If you also use configureXlsxWriterBeforeClose() to freeze rows, remember to account for the extra rows in setFreezeRow().

如果你想在其关闭前自定义 XLSX writer,你可以在导出器类上重写 configureXlsxWriterBeforeClosing()。该方法接收 Writer 实例作为参数,你可以在其关闭之前修改它。

use OpenSpout\Writer\XLSX\Entity\SheetView;
use OpenSpout\Writer\XLSX\Writer;

public function configureXlsxWriterBeforeClose(Writer $writer): Writer
{
    $sheetView = new SheetView();
    $sheetView->setFreezeRow(2);
    $sheetView->setFreezeColumn('B');
    
    $sheet = $writer->getCurrentSheet();
    $sheet->setSheetView($sheetView);
    $sheet->setName('export');
    
    return $writer;
}

Customizing the completion notification

When an export finishes, Filament sends a notification to the user who started it. You can customize the title and body of that notification by overriding getCompletedNotificationTitle() and getCompletedNotificationBody() on your exporter:

use Filament\Actions\Exports\Models\Export;

public static function getCompletedNotificationTitle(Export $export): string
{
    return 'Your product export is ready';
}

public static function getCompletedNotificationBody(Export $export): string
{
    return $export->successful_rows . ' products were exported.';
}

For anything beyond the title and body — for example, changing the notification color, adding extra actions, or replacing the icon — override modifyCompletedNotification(). You can either mutate the Notification passed in and return it, or build and return a completely new one:

use Filament\Actions\Action;
use Filament\Actions\Exports\Models\Export;
use Filament\Notifications\Notification;

public static function modifyCompletedNotification(Notification $notification, Export $export): Notification
{
    $notification->icon('heroicon-o-shopping-bag');

    if ($export->getOptions()['notifyTeam'] ?? false) {
        $notification->actions([
            ...$notification->getActions(),
            Action::make('shareWithTeam')
                ->url(route('exports.share', $export)),
        ]);
    }

    return $notification;
}

The Export model exposes the column mapping and options the user selected via $export->getColumnMap() and $export->getOptions(), so you can tailor the notification based on what the user exported.

自定义导出任务

处理导出任务的默认 job 是 Filament\Actions\Exports\Jobs\PrepareCsvExport。如果想继承该类并重写它的方法,你可以在服务提供者的 register() 方法中替换掉原来的类:

use App\Jobs\PrepareCsvExport;
use Filament\Actions\Exports\Jobs\PrepareCsvExport as BasePrepareCsvExport;

$this->app->bind(BasePrepareCsvExport::class, PrepareCsvExport::class);

或者,也可以将新的 Job 类传入到 Action 的 job() 方法中,以自定义特定导出任务:

use App\Filament\Exports\ProductExporter;
use App\Jobs\PrepareCsvExport;
use Filament\Actions\ExportAction;

ExportAction::make()
    ->exporter(ProductExporter::class)
    ->job(PrepareCsvExport::class)

自定义导出队列和连接

默认情况下,导出系统将使用默认队列和连接。如果你想自定义用于特定导出器的 Job 队列,你可以在你的导出器类中重写 getJobQueue() 方法:

public function getJobQueue(): ?string
{
    return 'exports';
}

重写你的导出类中的 getJobConnection() 方法,你也可以自定义用于特定导出器的 Job 连接:

public function getJobConnection(): ?string
{
    return 'sqs';
}

自定义导出任务中间件

默认情况下,导出系统每次导出时只会处理一个任务。这是为了防止服务器过载,也为了防止其他任务因大量导出而延迟。该功能在导出器类的 WithoutOverlapping 中间件中定义:

public function getJobMiddleware(): array
{
    return [
        (new WithoutOverlapping("export{$this->export->getKey()}"))->expireAfter(600),
    ];
}

如果你想自定义应用于某个导出器的 Job 所应用的中间件,你可以在导出类中重写此方法。更多关于 Job 中间件的信息,请查阅 Laravel 文档

自定义导出任务重试

默认情况下,导出系统会在 24 小时内重试一次任务,或者重试任务直到因未处理异常而失败 5 次时(以先发生为准)。这是为了解决临时问题,比如数据库不可用。你可以修改 Job 的重试时间周期,它在导出器类的 getJobRetryUntil() 方法中定义:

use Carbon\CarbonInterface;

public function getJobRetryUntil(): ?CarbonInterface
{
    return now()->addHours(12);
}

更多关于 Job 重试的信息,请查阅 Laravel 文档

自定义导出任务退避策略

默认情况下,导出系统在重试之前会等待 1 分钟,然后等待 2 分钟,随后是 5 分钟,最后是 10 分钟。这是为了防止服务器因任务反复失败而过载。此功能在导出器类的 getJobBackoff() 方法中定义:

/**
* @return int | array<int> | null
 */
public function getJobBackoff(): int | array | null
{
    return [60, 120, 300, 600];
}

你可以在 Laravel 文档中阅读有关任务退避的更多信息,包括如何配置指数退避。

自定义导出任务标签

默认情况下,导出系统会使用导入的 ID 来标记每个任务。这是为了方便轻你松找到与特定导出相关的所有任务。此功能在导出器类的 getJobTags() 方法中定义:

public function getJobTags(): array
{
    return ["export{$this->export->getKey()}"];
}

如果你想自定义应用于特定导出器作业的标签,你可以在导出器类中重写此方法。

自定义导出任务批次名

默认情况下,导出系统不会为任务批次定义任何名称。如果你想自定义应用于特定导出器任务批次的名称,可以在导出器类中重写 getJobBatchName() 方法:

public function getJobBatchName(): ?string
{
    return 'product-export';
}

授权

默认情况下,只有开启导出的用户才能下载器生成的文件。如果你想自定义授权逻辑,可以创建一个 ExportPolicy 类,并AuthServiceProvider 中注册它

use App\Policies\ExportPolicy;
use Filament\Actions\Exports\Models\Export;

protected $policies = [
    Export::class => ExportPolicy::class,
];

该策略的 view() 方法将用于授权访问下载。

请注意,如果你定义了策略,现有的逻辑(即只有开启导出的用户才可以访问)将被移除。如果你要保留该逻辑,你需要将其添加到策略中:

use App\Models\User;
use Filament\Actions\Exports\Models\Export;

public function view(User $user, Export $export): bool
{
    return $export->user()->is($user);
}

Security

Per-record authorization

The export system does not perform per-record authorization checks. When an export is triggered, all records matching the table query (or the model’s full dataset, if used outside a table) are included in the export without consulting your application’s Laravel policies. This means that if a user is allowed to trigger an export, they may receive records they would not normally be authorized to view through your application’s UI.

If you need to restrict which records are exported, you should scope the query using the modifyQueryUsing() method:

use Illuminate\Database\Eloquent\Builder;

ExportAction::make()
    ->exporter(ProductExporter::class)
    ->modifyQueryUsing(fn (Builder $query) => $query->whereBelongsTo(auth()->user()))

You could also apply global scopes to your model to ensure that only authorized records are ever queried.

NOTE

If your application has per-record visibility rules, you should scope the export query to ensure users only receive records they are authorized to view.

CSV formula injection

Filament’s export system writes data to CSV and XLSX files exactly as it is stored in the database, without any transformation. This means that if your database contains values beginning with characters like =, +, -, or @, they will appear unchanged in the exported file. When opened in spreadsheet software such as Microsoft Excel or Google Sheets, these values may be interpreted as formulas, which could pose a security risk if your data includes untrusted or user-submitted content. You should ensure that your users are aware of this risk, or sanitize the data before export using the formatStateUsing() method on each column, for example by prefixing values with a single quote (') to prevent formula interpretation.

Alternatively, you may opt in to Filament’s built-in protection. When enabled on a column, any value that begins with a formula-triggering character (=, +, -, @, a tab, or a carriage return) is automatically prefixed with a single quote (') so that spreadsheet software treats it as plain text. Enable it using the preventFormulaInjection() method on the column:

use Filament\Actions\Exports\ExportColumn;

ExportColumn::make('description')
    ->preventFormulaInjection()

If you would like to enable this protection for every export column across your application, you can use the configureUsing() method inside the boot() method of a service provider. Since this is applied to all columns, you can opt an individual column back out by passing false to preventFormulaInjection():

use Filament\Actions\Exports\ExportColumn;

ExportColumn::configureUsing(function (ExportColumn $column): void {
    $column->preventFormulaInjection();
});

// Opt a specific column back out:
ExportColumn::make('temperature')
    ->preventFormulaInjection(false)

NOTE

This protection is opt in and disabled by default, because prefixing a single quote alters legitimate data. For example, values such as -5 or a phone number like +44 1234 567890 are valid formula triggers and would be rewritten to '-5 and '+44 1234 567890. Only enable it when you are exporting untrusted or user-submitted content, and make sure the transformation is acceptable for the columns you enable it on.

Edit on GitHub

Still need help? Join our Discord community or open a GitHub discussion

Previous
Import action