Skip to content
全部文档

可配置的资源与页面

简介

有时你需要以不同配置多次注册同一个资源或页面。例如,「订单」资源可能在侧边栏中同时显示为「进行中的订单」和「已归档订单」,各自使用不同的查询作用域、导航标签和 URL slug——但共用同一个底层资源类。

可配置的资源与页面允许你在面板中多次注册同一个类,每次注册都有唯一的配置键和各自的选项。每个配置都有自己的路由、导航项和 URL slug,而资源或页面类可使用当前激活的配置在运行时调整行为。

INFO

虽然本指南位于插件章节,但可配置的资源与页面可在任意 PanelProvider 中使用——不必正在构建插件。它们对插件尤其有用,因为能让插件作者向用户暴露灵活的配置。

创建资源配置类

要让资源可配置,首先需要一个配置类。该类继承 ResourceConfiguration,并定义各次注册之间可以不同的选项:

php
use Filament\Resources\ResourceConfiguration;

class OrderResourceConfiguration extends ResourceConfiguration
{
    protected bool $isArchived = false;

    protected ?string $navigationLabel = null;

    protected ?string $navigationGroup = null;

    public function archived(bool $condition = true): static
    {
        $this->isArchived = $condition;

        return $this;
    }

    public function isArchived(): bool
    {
        return $this->isArchived;
    }

    public function navigationLabel(string $label): static
    {
        $this->navigationLabel = $label;

        return $this;
    }

    public function getNavigationLabel(): ?string
    {
        return $this->navigationLabel;
    }

    public function navigationGroup(string $group): static
    {
        $this->navigationGroup = $group;

        return $this;
    }

    public function getNavigationGroup(): ?string
    {
        return $this->navigationGroup;
    }
}

配置类遵循与 Filament 其余部分相同的 流畅 API 模式——setter 方法返回 $this 以便链式调用,getter 方法取出已存储的值。

TIP

ResourceConfiguration 基类已包含用于覆盖 URL slug 的 slug() 方法。你只需添加插件特有的属性。

将配置类关联到资源

在资源上设置 $configurationClass 属性,将其与配置类关联:

php
use Filament\Resources\Resource;

class OrderResource extends Resource
{
    protected static ?string $configurationClass = OrderResourceConfiguration::class;

    // ...
}

这会在资源上启用 make() 方法,用于创建可注册到面板上的新配置实例。

在面板上注册配置

你可以使用 make() 方法注册一个或多个配置。每个配置都需要唯一的键:

php
use App\Filament\Resources\OrderResource;

public function panel(Panel $panel): Panel
{
    return $panel
        ->resources([
            // "Active orders" configuration
            OrderResource::make('active')
                ->navigationLabel('Active Orders')
                ->navigationGroup('Orders'),
            // "Archived orders" configuration
            OrderResource::make('archived')
                ->navigationLabel('Archived Orders')
                ->navigationGroup('Orders')
                ->archived(),
        ]);
}

每次已配置的注册都会获得自己的路由和导航项。配置键('active''archived')在内部用于识别每次注册。

若还需要默认(未配置)的注册,也可以把资源类本身与配置一起注册。这是可选的——若只需要配置,也可以只注册配置:

php
$panel->resources([
    OrderResource::class, // Optional default registration
    OrderResource::make('active'),
    OrderResource::make('archived')
        ->archived(),
]);

TIP

若正在构建 插件类,则应在 register(Panel $panel) 方法内注册配置。完整示例见 在插件类中使用可配置资源

URL slug

单独注册资源类(不带配置)时,会使用资源的默认 URL slug——例如 /orders

使用键注册配置时,该键会追加到资源的基础 slug 之后。例如,OrderResource::make('active') 可在 /orders/active 访问,OrderResource::make('archived') 则可在 /orders/archived 访问。

你可以使用 slug() 覆盖配置的整个 slug,而不使用默认的 {base}/{key} 模式:

php
OrderResource::make('archived')
    ->slug('order-archive') // accessible at `/order-archive` instead of `/orders/archived`
    ->archived(),

在运行时使用配置

在资源类内部,调用 static::getConfiguration() 可获取当前请求的激活配置。当通过默认(未配置)注册访问资源时,该方法返回 null

php
use Filament\Resources\Resource;
use Illuminate\Database\Eloquent\Builder;
use UnitEnum;

class OrderResource extends Resource
{
    protected static ?string $configurationClass = OrderResourceConfiguration::class;

    public static function getEloquentQuery(): Builder
    {
        $query = parent::getEloquentQuery();

        if ($configuration = static::getConfiguration()) {
            if ($configuration->isArchived()) {
                $query->where('archived_at', '!=', null);
            }
        }

        return $query;
    }

    public static function getNavigationLabel(): string
    {
        if ($configuration = static::getConfiguration()) {
            if ($label = $configuration->getNavigationLabel()) {
                return $label;
            }
        }

        return parent::getNavigationLabel();
    }

    public static function getNavigationGroup(): string | UnitEnum | null
    {
        if ($configuration = static::getConfiguration()) {
            if ($group = $configuration->getNavigationGroup()) {
                return $group;
            }
        }

        return parent::getNavigationGroup();
    }

    // ...
}

你可以使用 static::hasConfiguration() 作为简写,检查当前是否有激活的配置:

php
if (static::hasConfiguration()) {
    // Running inside a configured registration
}

为特定配置生成 URL

为已配置的资源生成 URL 时,向 getUrl() 传入 configuration 参数:

php
// URL for the default (unconfigured) registration
OrderResource::getUrl();

// URL for the "active" configuration
OrderResource::getUrl(configuration: 'active');

// URL for a specific page within the "archived" configuration
OrderResource::getUrl('edit', ['record' => $order], configuration: 'archived');

当你已处于已配置的请求中(例如在资源页面内)时,getUrl() 会自动使用当前配置上下文。仅在要链接到与当前不同的配置时,才需要传入 configuration 参数。

可配置页面

页面遵循与资源相同的模式。主要区别是:

  • 配置类继承 `PageConfiguration` 而非 `ResourceConfiguration`
  • 使用 `$panel->pages()` 而非 `$panel->resources()` 注册配置
  • 由于页面是 Livewire 组件,可以在 `mount()` 中读取配置值:
php
use Filament\Pages\Page;

class SettingsPage extends Page
{
    protected static ?string $configurationClass = SettingsPageConfiguration::class;

    public function mount(): void
    {
        if ($configuration = static::getConfiguration()) {
            $this->settingsCategory = $configuration->getSettingsCategory();
        }
    }

    // ...
}
php
$panel->pages([
    SettingsPage::make('general')
        ->slug('general-settings')
        ->settingsCategory('general'),
    SettingsPage::make('advanced')
        ->slug('advanced-settings')
        ->settingsCategory('advanced'),
]);

临时切换配置上下文

你可以使用 withConfiguration() 在特定配置的上下文中执行代码。当你需要为当前激活配置以外的注册生成 URL 或访问配置值时,这会很有用:

php
$archivedUrl = OrderResource::withConfiguration('archived', function () {
    return OrderResource::getUrl('index');
});

在插件类中使用可配置资源

下面是一个向用户暴露可配置资源的插件完整示例:

php
use Filament\Contracts\Plugin;
use Filament\Panel;

class TasksPlugin implements Plugin
{
    /** @var array<TaskResourceConfiguration> */
    protected array $taskResourceConfigurations = [];

    public static function make(): static
    {
        return app(static::class);
    }

    public function getId(): string
    {
        return 'tasks';
    }

    /**
     * @param  array<TaskResourceConfiguration>  $configurations
     */
    public function taskResources(array $configurations): static
    {
        $this->taskResourceConfigurations = $configurations;

        return $this;
    }

    public function register(Panel $panel): void
    {
        $panel->resources([
            TaskResource::class,
            ...$this->taskResourceConfigurations,
        ]);
    }

    public function boot(Panel $panel): void
    {
        //
    }
}

插件用户随后可以注册多个任务视图:

php
use Vendor\TasksPlugin\TasksPlugin;
use Vendor\TasksPlugin\TaskResource;

public function panel(Panel $panel): Panel
{
    return $panel
        ->plugin(
            TasksPlugin::make()
                ->taskResources([
                    TaskResource::make('my-tasks')
                        ->ownedByCurrentUser(),
                    TaskResource::make('team-tasks')
                        ->ownedByCurrentTeam(),
                ])
        );
}