#14 FilamentPHP応用
カスタムページを作る——Resourceと独立したPageの作り方
カスタムページが必要な場面
FilamentのResourceは「一覧・作成・編集」という決まったパターンを提供しますが、それ以外のページが必要になる場面があります。
- 設定画面(アプリケーション全体の設定を管理するページ)
- ダッシュボード以外のカスタム分析ページ
- 複数モデルにまたがる複合的な操作ページ
- APIのテストやデバッグ用の管理ツールページ
make:filament-page でページを生成
php artisan make:filament-page Settings
生成ファイル:
app/Filament/Pages/Settings.php
resources/views/filament/pages/settings.blade.php
基本的なPageクラスの構造
namespace App\Filament\Pages;
use Filament\Pages\Page;
use Filament\Actions\Action;
class Settings extends Page
{
protected static ?string $navigationIcon = 'heroicon-o-cog-6-tooth';
protected static ?string $navigationLabel = '設定';
protected static ?string $title = 'アプリケーション設定';
protected static ?int $navigationSort = 99;
protected static ?string $navigationGroup = '管理';
protected static string $view = 'filament.pages.settings';
// ナビゲーションバッジ(オプション)
public static function getNavigationBadge(): ?string
{
return null;
}
// アクセス制御
public static function canAccess(): bool
{
return auth()->user()?->hasRole('admin') ?? false;
}
}
フォームを持つカスタムページ
設定ページのようにフォームを内包するページを作る場合は、HasFormsトレイトを使います。
namespace App\Filament\Pages;
use Filament\Forms\Concerns\InteractsWithForms;
use Filament\Forms\Contracts\HasForms;
use Filament\Forms\Form;
use Filament\Forms\Components\TextInput;
use Filament\Forms\Components\Toggle;
use Filament\Forms\Components\Section;
use Filament\Notifications\Notification;
use Filament\Pages\Page;
use App\Models\Setting;
class Settings extends Page implements HasForms
{
use InteractsWithForms;
protected static ?string $navigationIcon = 'heroicon-o-cog-6-tooth';
protected static ?string $navigationLabel = '設定';
protected static string $view = 'filament.pages.settings';
// フォームデータを保持するpublicプロパティ
public ?array $data = [];
public function mount(): void
{
// 既存の設定を読み込んでフォームに注入
$this->form->fill([
'site_name' => Setting::get('site_name', 'My App'),
'admin_email' => Setting::get('admin_email'),
'maintenance_mode' => Setting::get('maintenance_mode', false),
'posts_per_page' => Setting::get('posts_per_page', 15),
]);
}
public function form(Form $form): Form
{
return $form
->schema([
Section::make('基本設定')
->schema([
TextInput::make('site_name')
->label('サイト名')
->required()
->maxLength(100),
TextInput::make('admin_email')
->label('管理者メール')
->email()
->required(),
]),
Section::make('表示設定')
->schema([
TextInput::make('posts_per_page')
->label('1ページあたりの記事数')
->numeric()
->minValue(5)
->maxValue(100)
->default(15),
]),
Section::make('メンテナンス')
->schema([
Toggle::make('maintenance_mode')
->label('メンテナンスモード')
->helperText('有効にすると管理者以外はサイトにアクセスできません'),
]),
])
->statePath('data');
}
public function save(): void
{
$data = $this->form->getState();
foreach ($data as $key => $value) {
Setting::set($key, $value);
}
Notification::make()
->title('設定を保存しました')
->success()
->send();
}
protected function getFormActions(): array
{
return [
Action::make('save')
->label('保存する')
->submit('save'),
];
}
}
Bladeテンプレート
<x-filament-panels::page>
<x-filament::section>
<form wire:submit="save">
{{ $this->form }}
<div class="mt-6 flex justify-end gap-3">
{{ $this->saveAction }}
</div>
</form>
</x-filament::section>
</x-filament-panels::page>
ヘッダーアクションの定義
protected function getHeaderActions(): array
{
return [
Action::make('resetDefaults')
->label('デフォルトに戻す')
->color('gray')
->requiresConfirmation()
->action(function (): void {
Setting::resetAll();
$this->mount(); // フォームを再読み込み
Notification::make()
->title('デフォルト設定に戻しました')
->info()
->send();
}),
];
}
ResourceのカスタムPage(ResourceにPage追加)
既存Resourceに独自ページを追加することもできます。
php artisan make:filament-page BulkImport --resource=PostResource
namespace App\Filament\Resources\PostResource\Pages;
use App\Filament\Resources\PostResource;
use Filament\Resources\Pages\Page;
class BulkImport extends Page
{
protected static string $resource = PostResource::class;
protected static ?string $navigationLabel = '一括インポート';
protected static string $view = 'filament.resources.post-resource.pages.bulk-import';
public static function getNavigationItems(array $urlParameters = []): array
{
// サイドバーには表示しない(ヘッダーアクションから遷移させる場合)
return [];
}
}
Resourceのページ一覧に追加:
// app/Filament/Resources/PostResource.php
public static function getPages(): array
{
return [
'index' => Pages\ListPosts::route('/'),
'create' => Pages\CreatePost::route('/create'),
'edit' => Pages\EditPost::route('/{record}/edit'),
'bulk-import' => Pages\BulkImport::route('/bulk-import'),
];
}
コツ・注意点・ハマりポイント
コツ: フォームを持つページでは->statePath('data')を使ってLivewireのプロパティ$dataにバインドします。これを忘れるとフォームの値がLivewireコンポーネントに正しく反映されません。
注意点: mount()はページの初期表示時に1回だけ呼ばれます。フォームの初期値のセットは必ずmount()内で$this->form->fill()を使って行ってください。
ハマりポイント: getFormActions()とgetHeaderActions()は別物です。getFormActions()はフォームの下にボタンを表示し、getHeaderActions()はページタイトルの右横に表示します。保存ボタンはフォームアクション、追加の操作はヘッダーアクションとして使い分けると直感的なUIになります。
まとめ
FilamentのカスタムPageはPageクラスを継承するだけで作成でき、HasFormsトレイトを追加することでフォームも統合できます。設定画面・ツールページ・カスタム分析画面など、ResourceのCRUDパターンに収まらないあらゆるページをFilamentの統一されたUIで構築できます。