编程

Filament v4 Beta 升级指南

24 2025-06-24 19:29:00

要求

  • PHP 8.2+
  • Laravel v11.28+
  • Tailwind CSS v4.0+,如果你当前的 Filament 应用使用的是 CSS v3.0。如果你只是使用没有自定义主题 CSS 文件的 Filament 面板,则此项不适用。
  • Filament 不再需要 doctrine/dbal,不过如果你的应用需要它,但没有直接安装,你应该将其加入到 composer.json 文件中

运行自动升级脚本

注:升级脚本不能替代升级指南。它可以处理升级指南中没有提到的许多小更改,但它不能处理所有破坏性的更改。你仍然应该阅读手动升级步骤,看看需要对代码进行哪些更改。

首先是运行自动升级脚本。因 Filament v4 仍处于 beta 测试阶段,你需要在安装前在 composer.json 中将 minimum-stability 设置为 beta。也可以通过 CLI 对其手动调整。

composer config minimum-stability beta

composer.json 中代码:

{
    "minimum-stability": "beta"
}

该脚本将自动将你的应用升级到最新版的 Filament 应用,并修改你的代码、处理大部分破坏性更新:

composer require filament/upgrade:"^4.0" -W --dev

vendor/bin/filament-v4

注:当使用 Windows PowerShell 来安装 Filament 时,你可能需要运行如下命令,由于它在版本约束中忽视了 ^ 字符:

composer require filament/upgrade:"~4.0" -W --dev

vendor/bin/filament-v4

如果升级脚本安装失败,请确保你的 PHPStan 版本至少是 v2 或者 Larastan 版本至少为 v3。该版本使用 Rector v2、其要求 PHPStan v2 以上版本。

确保仔细遵循说明,并查看脚本所做的更改。之后,你可能需要对代码进行一些手动更改,但脚本应该已经为你处理大部分重复工作。

现在,可以 composer remove filament/upgrade,因为你不再需要它了。

注:你所使用的一些插件可能还不支持 v4。你可以临时将它从 composer.json 文件中删除,使用兼容 v4 的类似插件替换,或者等待插件更新再升级你的应用,深圳可以编写 PR 帮助作者升级。

发布配置文件

Filament v4 中的一些更改可以使用配置文件恢复。如果您尚未发布配置文件,可以通过运行以下命令来发布:

php artisan vendor:publish --tag=filament-config

首先,v4 中的 default_filesystem_disk 设置成 FILESYSTEM_DISK 而非 FILAMENT_FILESYSTEM_DISK。如果要保留 v3 中的行为,请使用如下设置:

return [

    // ...

    'default_filesystem_disk' => env('FILAMENT_FILESYSTEM_DISK', 'public'),

    // ...

]

v4 对 Filament 如何生成文件作了一些调整。file_generation 被添加到了 v4 配置文件中,以便你想保持新代码与升级前一致时,能回到 v3 的风格,如果你的配置文件中还没有 file_generation 选项,你可以自己添加或者重新发布配置文件并根据需要调整其中内容:

use Filament\Support\Commands\FileGenerators\FileGenerationFlag;

return [

    // ...

    'file_generation' => [
        'flags' => [
            FileGenerationFlag::EMBEDDED_PANEL_RESOURCE_SCHEMAS, // Define new forms and infolists inside the resource class instead of a separate schema class.
            FileGenerationFlag::EMBEDDED_PANEL_RESOURCE_TABLES, // Define new tables inside the resource class instead of a separate table class.
            FileGenerationFlag::PANEL_CLUSTER_CLASSES_OUTSIDE_DIRECTORIES, // Create new cluster classes outside of their directories.
            FileGenerationFlag::PANEL_RESOURCE_CLASSES_OUTSIDE_DIRECTORIES, // Create new resource classes outside of their directories.
            FileGenerationFlag::PARTIAL_IMPORTS, // Partially import components such as form fields and table columns instead of importing each component explicitly.
        ],
    ],

    // ...

]

必须手动处理的破坏性更新

影响较大的变化

文件可见性现在默认是私有(private)

除了默认的磁盘改成了 local 外,默认情况下,各个组件的文件可见性设置已更改为 private ,而不是 public

当 Filament 首次创建时,Laravel 没有为本地文件生成临时签名 URL 的方法。因此,Filament 的默认磁盘设置为 public,文件上传的可见性也设置为 public 以简化开发体验,而无需额外配置。

然而,Laravel 11 引入了一个新的“本地临时 URL”功能,该功能默认启用。在添加此功能之前创建项目的用户可能必须更新其config/filesystems.php 文件才能启用它。

在 v4 中,Filament 的默认磁盘设置为 local,文件上传的可见性默认设置为 private。这意味着默认情况下文件不可公开访问,你需要生成一个临时签名的 URL 来访问它们。此更改会影响以下组件:

  • FileUpload 表单字段,包括 SpatieMediaLibraryFileUpload
  • ImageColumn 表格列字段,包括 SpatieMediaLibraryImageColumn
  • ImageEntry infolist entry,包括 SpatieMediaLibraryImageEntry

Tip:通过在 AppServiceProvider 等服务提供者的 boot() 方法中添加以下代码,你可以在整个应用中保留旧的默认行为:

use Filament\Forms\Components\FileUpload;
use Filament\Infolists\Components\ImageEntry;
use Filament\Tables\Columns\ImageColumn;

FileUpload::configureUsing(fn (FileUpload $fileUpload) => $fileUpload
    ->visibility('public'));

ImageColumn::configureUsing(fn (ImageColumn $imageColumn) => $imageColumn
    ->visibility('public'));

ImageEntry::configureUsing(fn (ImageEntry $imageEntry) => $imageEntry
    ->visibility('public'));

自定义主题需要升级到 Tailwind CSS v4

之前,自定义主题的 CSS 文件应该包含这些:

@import '../../../../vendor/filament/filament/resources/css/theme.css';

@config 'tailwind.config.js';

现在,应该包含这些:

@import '../../../../vendor/filament/filament/resources/css/theme.css';

@source '../../../../app/Filament';
@source '../../../../resources/views/filament';

这将加载 Tailwind CSS。@source 告诉 Tailwind 在哪里可以找到你应用中使用的类。你应该在老的 tailwind.config.js 文件中检查 content 路径,并将像这样将它们添加为 @source 条目。你不需要引入 vendor/filament 作为 @source,不过请检查你安装的插件查看它们是否需要 @source 条目。

最后,你应该使用 Tailwind 升级工具自动调整配置文件以使用 Tailwind v4,并安装 Tailwind v4  包以替换 Tailwind v3 包:

npx @tailwindcss/upgrade

用于主题的 tailwind.config.js 文件不再使用,因为 Tailwind CSS v4 在 CSS 中定义了配置。你对 tailwind.config.js 文件所做的任何自定义都应该添加到 CSS 文件中。

表格过滤器变更为默认延迟加载

Filament v3 的 deferFilters() 方法现在在 Filament v4 中是默认行为。因此,用户点击按钮之后,过滤器才会应用到表格中。要禁用该行为,你可以使用 deferFilters(false) 方法。

use Filament\Tables\Table;

public function table(Table $table): Table
{
    return $table
        ->deferFilters(false);
}

注意:在 AppServiceProvider 等服务提供者的 boot() 方法中添加以下代码,你可以在整个应用中保留旧的默认行为:

use Filament\Tables\Table;

Table::configureUsing(fn (Table $table) => $table
    ->deferFilters(false));

GridSectionFieldset 布局组件现在默认步占有整个列宽。

在 v3 中,GridSectionFieldset 布局组件默认消费了其父级网格的所有宽度。这与 Filament 中其他所有组件的行为不一致,默认情况下,Filament 只消耗一列网格。其目的是使这些组件更容易集成到默认的 Filament 资源表单和 infolist 中,也即使用开箱即用的两列网格。

在 v4 中,GridSectionFieldset 布局组件现在默认只消费一列网格。如果你希望它们占有所有列宽,请使用l columnSpanFull() 方法:

use Filament\Schemas\Components\Fieldset;
use Filament\Schemas\Components\Grid;
use Filament\Schemas\Components\Section;

Fieldset::make()
    ->columnSpanFull()
    
Grid::make()
    ->columnSpanFull()

Section::make()
    ->columnSpanFull()

Tip:通过在 AppServiceProvider 等服务提供者的 boot() 方法中添加以下代码,你可以在整个应用中保留旧的默认行为:

use Filament\Schemas\Components\Fieldset;
use Filament\Schemas\Components\Grid;
use Filament\Schemas\Components\Section;

Fieldset::configureUsing(fn (Fieldset $fieldset) => $fieldset
    ->columnSpanFull());

Grid::configureUsing(fn (Grid $grid) => $grid
    ->columnSpanFull());

Section::configureUsing(fn (Section $section) => $section
    ->columnSpanFull());

unique() 验证规则默认忽略 Eloquent 记录

在 v3 中 ,unique() 方法验证默认不忽略当前表格 Eloquent 记录。该行为通过 ignoreRecord: true 参数启用,或者传入一个自定义的ignorable 记录。

在 v4 中,unique() 方法的 ignoreRecord 参数默认为 true

如果你之前使用了 unique() 验证规则而未使用 ignoreRecord 或者 ignorable 参数,你应该使用 ignoreRecord: false 来禁用该新行为。

Tip:通过在 AppServiceProvider 等服务提供者的 boot() 方法中添加以下代码,你可以在整个应用中保留旧的默认行为:

use Filament\Forms\Components\Field;

Field::configureUsing(fn (Field $field) => $field
    ->uniqueValidationIgnoresRecordByDefault(false));

all 分页选项现在默认对表格不可用

all 分页选项现在默认对表格不可用。如果你想要将其用到表格上,你可以将其添加到配置中:

use Filament\Tables\Table;

public function table(Table $table): Table
{
    return $table
        ->paginationPageOptions([5, 10, 25, 50, 'all']);
}

请注意使用 all 处理大量记录会带来性能问题。

Tip:通过在 AppServiceProvider 等服务提供者的 boot() 方法中添加以下代码,你可以在整个应用中保留旧的默认行为:

use Filament\Tables\Table;

Table::configureUsing(fn (Table $table) => $table
    ->paginationPageOptions([5, 10, 25, 50, 'all']));

中度影响的变更

自动租户全局 Scope 和关联

在 v3 中使用租户时,Filament 只对当前租户限定资源查询范围:以渲染资源表格、解析 URL 参数以及获取全局舒适结果。在许多情况下,面板中的其他查询默认情况下没有限定查询范围,开发人员必须手动限定。虽然这是一个有文档化的特性,但它为开发人员带来了很多额外的工作。

在 v4 中,Filament 会自动将面板中的所有查询范围限定为当前租户,并使用模型事件自动将新记录与当前租户相关联。这意味着在大多数情况下,你不再需要手动限定查询范围或关联新的 Eloquent 记录。还有一些重要的问题需要考虑,因此文件已经更新以反映这一点。

Radio inline() 方法行为

在 v3 中,inline() 方法将 Radio 按钮与其他同类串联在同一行,同时也与其标签串联。这与其他组件是不一致的。

在 v3 中,inline() 方法只将 Radio 按钮与其他同类 Radio 串联在同一行 inline(),而不包括标签。如果你想让其与标签内联在一起,可以使用 inlineLabel() 方法。

如果你之前使用 inline()->inlineLabel(false) 来达成 v4 的默认行为,你现在可以只使用 inline()

Tip:通过在 AppServiceProvider 等服务提供者的 boot() 方法中添加以下代码,你可以在整个应用中保留旧的默认行为:

use Filament\Forms\Components\Radio;

Radio::configureUsing(fn (Radio $radio) => $radio
    ->inlineLabel(fn (): bool => $radio->isInline()));

导入和导出作业重试

在 Filament v3 中,如果导入和导出作业失败,则会连续重试 24 小时,默认情况下重试没有间隔事件。这给一些用户带来了问题,因为没有间隔,而且作业可能会很快重试,导致队列中充斥着不断失败的作业。

在 v4 中,它们被重试 3 次,每次重试之间有 60 秒的回退。

该行为可以在导入器和导出器类中自定义。

低影响变化

ImageColumn::limitedRemainingText()ImageEntry::limitedRemainingText()isSeparate 参数被移除

以前,用户可以使用 isSeparate 参数将有限图像的数量单独显示到图像堆栈中。现在该参数已被删除,如果存在堆栈,文本将始终堆叠在顶部,而不是分开。如果图像没有堆叠,文本将是单独的。

RichEditor 组件的 disableGrammarly() 方法已经移除

disableGrammarly() 方法已经从 RichEditor 组件中移除。该方法用于禁用 Grammarly 浏览器在该编辑器上使用。由于将底层的实现从 Trix 移动到 TipTap 后,没有找到在编辑器上禁用 Grammarly 的方式。

 

重写 Field::make(), MorphToSelect::make(), Placeholder::make()Builder\Block::make() 方法

Field::make()MorphToSelect::make(),、Placeholder::make()Builder\Block::make() 方法的签名已经改变。继承 Field, MorphToSelect, PlaceholderBuilder\Block 的类和重写 make() 方法的类必须更新方法签名以匹配新的签名。新签名如下:

public static function make(?string $name = null): static

此变更是因为引入了 getDefaultName() 方法,如果没有指定值(null),可以通过重写它来提供默认的 $name 值如果你此前重写了 make() 方法来提供默认的 $name 值,现在建议通过重写 getDefaultName() 方法来实现,以避免将来的维护负担: 

public static function getDefaultName(): ?string
{
    return 'default';
}

如果你重写了 make() 方法,以便在其初始化时传入默认配置,则推荐改为重写 stepUp() 方法,该方法在对象初始化之后立即调用。

protected function setUp(): void
{
    parent::setUp();

    $this->label('Default label');
}

理想情况下,你应该避免重写 make() 方法,因为还有 setUp() 等替代方法,如果 Filament 决定在未来引入新的构造函数参数,这样做会导致你的代码变得脆弱。

重写 Entry::make() 方法

Entry::make() 方法的签名已经改变。任何继承 Entry 类和重写 make() 方法的类都必须更新其签名使之匹配新的签名。新签名如下:

public static function make(?string $name = null): static

此变更是因为引入了 getDefaultName() 方法,如果没有指定值(null),可以通过重写它来提供默认的 $name 值。如果你此前重写了 make() 方法来提供默认的 $name 值,现在建议通过重写 getDefaultName() 方法来实现,以避免将来的维护负担: 

public static function getDefaultName(): ?string
{
    return 'default';
}

如果你重写了 make() 方法,以便在其初始化时传入默认配置,则推荐改为重写 stepUp() 方法,该方法在对象初始化之后立即调用。

protected function setUp(): void
{
    parent::setUp();

    $this->label('Default label');
}

理想情况下,你应该避免重写 make() 方法,因为还有 setUp() 等替代方法,如果 Filament 决定在未来引入新的构造函数参数,这样做会导致你的代码变得脆弱。

重写 Column::make()Constraint::make() 方法

Column::make()Constraint::make() 方法的签名已经改变。任何继承 Column::make()Constraint::make()  类和重写 make() 方法的类都必须更新其签名使之匹配新的签名。新签名如下

public static function make(?string $name = null): static

此变更是因为引入了 getDefaultName() 方法,如果没有指定值(null),可以通过重写它来提供默认的 $name 值。如果你此前重写了 make() 方法来提供默认的 $name 值,现在建议通过重写 getDefaultName() 方法来实现,以避免将来的维护负担: 

public static function getDefaultName(): ?string
{
    return 'default';
}

如果你重写了 make() 方法,以便在其初始化时传入默认配置,则推荐改为重写 stepUp() 方法,该方法在对象初始化之后立即调用。

protected function setUp(): void
{
    parent::setUp();

    $this->label('Default label');
}

理想情况下,你应该避免重写 make() 方法,因为还有 setUp() 等替代方法,如果 Filament 决定在未来引入新的构造函数参数,这样做会导致你的代码变得脆弱。

重写 ExportColumn::make()ImportColumn::make() 方法

ExportColumn::make()ImportColumn::make() 方法的签名已经改变。任何继承 ExportColumnImportColumn  类和重写 make() 方法的类都必须更新其签名使之匹配新的签名。新签名如下:

public static function make(?string $name = null): static

此变更是因为引入了 getDefaultName() 方法,如果没有指定值(null),可以通过重写它来提供默认的 $name 值。如果你此前重写了 make() 方法来提供默认的 $name 值,现在建议通过重写 getDefaultName() 方法来实现,以避免将来的维护负担: 

public static function getDefaultName(): ?string
{
    return 'default';
}

如果你重写了 make() 方法,以便在其初始化时传入默认配置,则推荐改为重写 stepUp() 方法,该方法在对象初始化之后立即调用。

protected function setUp(): void
{
    parent::setUp();

    $this->label('Default label');
}

理想情况下,你应该避免重写 make() 方法,因为还有 setUp() 等替代方法,如果 Filament 决定在未来引入新的构造函数参数,这样做会导致你的代码变得脆弱。

在导入和导出作业中对用户进行身份验证

在 v3 中,Illuminate\Auth\Events\Login 事件在导入和导出 Job 中触发,以设置当前用户。v4 没有了这种情况:用户是认证了,但没有触发事件,以避免任何只对实际登录用户运行的监听器的运行。

重写 Resource, RelationManager or ManageRelatedRecords 类 can*() 授权方法

虽然这些方法(如 canCreate(), canViewAny()canDelete() 没有记入文档,在 v3 中重写这些类可以提供自定义授权逻辑,你应该清楚的是,他们在 v4 中不总是被调用。新版本中授权被改进来正确支持策略,而这些方法太过简单因为他们只能返回布尔值。

如果你能够在模型策略中实现自定义授权,请使用该方式实现。如果你需要在资源或资源管理类中自定义授权逻辑,你应该重写 get*AuthorizationResponse() 方法,比如  getCreateAuthorizationResponse(), getViewAnyAuthorizationResponse()getDeleteAuthorizationResponse()。这些方法在授权逻辑执行时调用,并返回策略响应对象。如果你删除了重写的 can*() 方法, get*AuthorizationResponse() 方法将用于确定授权响应布尔值,因此你无需两次维护其逻辑。

欧洲葡萄牙语翻译

欧洲葡萄牙语已经从 pt_PT 改为 pt,其为 Laravel 社区中更常用的语言代码。 

尼泊尔语翻译

尼泊尔语翻译已经从 np 改为 ne,其为 Laravel 社区中更常用的语言代码。

挪威语翻译

挪威语翻译翻译已经从 no 改为 nb,其为 Laravel 社区中更常用的语言代码。

高棉语翻译

高棉语翻译已经从 kh 改为 km,其为 Laravel 社区中更常用的语言代码。

一些已弃用的表格配置方法已被删除

Filament v3 之前,表格可以通过重写 Livewire 组件类方法来配置,而不是修改 $table 配置对象。这在 v3 中弃用,在 v4 中删除了。如果你使用以下这些方法,应该将其从 Livewire 组件类中删除,而使用对应的 $table 配置方法:

  • getTableRecordUrlUsing() 应该替换成 $table->recordUrl()
  • getTableRecordClassesUsing() 应该替换成 $table->recordClasses()
  • getTableRecordActionUsing() 应该替换成 $table->recordAction()
  • isTableRecordSelectable() 应该替换成 $table->checkIfRecordIsSelectableUsing()

 

下一篇