升级指南
NOTE
如果你发现该指南中有任何缺失,可以发送 PR 到仓库。
新要求
- PHP 8.2+
- Laravel v11.28+
- Tailwind CSS v4.0+,如果你当前的 Filament 应用使用的是 CSS v3.0。如果你只是使用没有自定义主题 CSS 文件的 Filament 面板,则此项不适用。
- Filament 不再需要 doctrine/dbal,不过如果你的应用需要它,但没有直接安装,你应该将其加入到
composer.json
文件中
运行自动升级脚本
NOTE
升级脚本不能替代升级指南。它可以处理升级指南中没有提到的许多小更改,但它不能处理所有破坏性的更改。你仍然应该阅读手动升级步骤,看看需要对代码进行哪些更改。
首先是运行自动升级脚本。因 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
NOTE
当使用 Windows PowerShell 来安装 Filament 时,你可能需要运行如下命令,由于它在版本约束中忽视了 ^
字符:
composer require filament/upgrade:"~4.0" -W --dev
vendor/bin/filament-v4
NOTE
如果升级脚本安装失败,请确保你的 PHPStan 版本至少是 v2 或者 Larastan 版本至少为 v3。该版本使用 Rector v2、其要求 PHPStan v2 以上版本。
确保仔细遵循升级说明,并查看脚本所做的更改。之后,你可能需要对代码进行一些手动更改,但脚本应该已经为你处理大部分重复工作。
然后,你就可以运行 composer remove filament/upgrade
,因为你不再需要它了。
NOTE
注:你所使用的一些插件可能还不支持 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.
],
],
// ...
]
必须手动处理的破坏性改动
开始前,选择你项目中使用的包,以为你指定的需求过滤升级指南:
该包也常在面板中使用,或者同时使用表格包及 Action 包
该包也常在面板中使用,或者同时使用表格包及 Action 包。
该包也常在面板中使用。
该包也常在面板中使用。
该包也常在面板中使用。
该包也常在面板中使用。
高影响变更
文件可见性现在默认为私有
除了将默认的磁盘改成了 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
信息列表 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 升级工具]((https://tailwindcss.com/docs/upgrade-guide#using-the-upgrade-tool)自动调整配置文件以使用 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);
}
TIP
通过在 AppServiceProvider
等服务提供者的 boot()
方法中添加以下代码,你可以在整个应用中保留旧的默认行为:
use Filament\Tables\Table;
Table::configureUsing(fn (Table $table) => $table
->deferFilters(false));
Grid
, Section
和 Fieldset
布局组件现在默认步占有整个列宽
在 v3 中,Grid
、Section
和 Fieldset
布局组件默认消费了其父级网格的所有宽度。这与 Filament 中其他所有组件的行为不一致,默认情况下,Filament 只消耗一列网格。其目的是使这些组件更容易集成到默认的 Filament 资源表单和 infolist 中,也即使用开箱即用的两列网格。
在 v4 中,Grid
、Section
和 Fieldset
布局组件现在默认只消费一列网格。如果你希望它们占有所有列宽,请使用 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());
验证规则默认忽略 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']));
官方 Spatie Translatable 插件现已弃用
去年,Filament 团队决定将 Spatie Translatable 插件转交给 Lara Zeus 团队,它是许多 Filament 插件的可靠开发者。从那之后,他们维护了该插件的 fork。
因此,官方 Spatie Translatable 插件将不接受 v4 支持,现已弃用。你可以使用 Lara Zeus Translatable 插件作为替代方案。该插件与 Spatie Translatable 官方插件同个版本号兼容,且已经通过 Filament v4 测试。它同时也修复了官方插件中长期存在的一些插件。
自动升级脚本建立命令行卸载官方插件并安装 Lara Zeus 插件,并将代码中的引用从官方插件改成 Lara zeus 插件。
中等影响变更
自动化租户全局 Scope 和关联
在 v3 中使用租户时,Filament 只对当前租户限定资源查询范围(Scope):以渲染资源表格、解析 URL 参数以及获取全局搜索结果。在许多情况下,面板中的其他查询默认情况下没有限定查询范围,开发人员必须手动限定。虽然这是一个有文档化的特性,但它为开发人员带来了很多额外的工作。
在 v4 中,Filament 会自动将面板中的所有查询范围限定为当前租户,并使用模型事件自动将新记录与当前租户相关联。这意味着在大多数情况下,你不再需要手动限定查询范围或关联新的 Eloquent 记录。还有一些重要的问题需要考虑,因此文档已经更新以反映这一点。
Radio inline() 方法行为
在 v3 中,inline()
方法将 Radio 按钮与其他同类串联在同一行,同时也与其标签串联。这与其他组件是不一致的。
在 v4 中,inline()
方法只将 Radio 按钮与其他同类 Radio 串联在同一行,而不包括标签。如果你想让其与标签内联在一起,可以使用 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()
, and Builder\Block::make()
方法的签名已经改变。任何继承 Field
, MorphToSelect
, Placeholder
或 Builder\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()
方法的签名已经改变。任何继承 ExportColumn
或 ImportColumn
类和重写 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 决定在未来引入新的构造函数参数,这样做会导致你的代码变得脆弱。
Authenticating the user inside the import and export jobs
In v3, the Illuminate\Auth\Events\Login
event was fired from the import and export jobs, to set the current user. This is no longer the case in v4: the user is authenticated, but that event is not fired, to avoid running any listeners that should only run for actual user logins.
重写 Resource
, RelationManager
或 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()
Still need help? Join our Discord community or open a GitHub discussion