#03 FilamentPHP基礎

フォームフィールドを使いこなす——入力UIの全体像

フォームフィールドの全体像

Filamentのフォームは Forms\Components 名前空間以下に多数のフィールドクラスが用意されています。すべてのフィールドは make('カラム名') で生成し、メソッドチェーンでオプションを追加していきます。

use Filament\Forms\Components\TextInput;

TextInput::make('email')      // カラム名(必須)
    ->label('メールアドレス') // ラベル文字列
    ->required()              // 必須バリデーション
    ->email();                // メール形式バリデーション

TextInput

最も基本的なフィールドです。多彩な入力タイプに対応しています。

use Filament\Forms\Components\TextInput;

// 基本的なテキスト入力
TextInput::make('name')
    ->label('名前')
    ->placeholder('山田 太郎')
    ->maxLength(100),

// メールアドレス
TextInput::make('email')
    ->label('メールアドレス')
    ->email()           // type="email" に変更 + バリデーション
    ->unique(),         // DBのuniqueバリデーション

// パスワード
TextInput::make('password')
    ->label('パスワード')
    ->password()        // 入力を隠す
    ->revealable()      // 表示/非表示の切り替えボタン
    ->minLength(8),

// 数値入力
TextInput::make('price')
    ->label('価格')
    ->numeric()         // 数値のみ許可
    ->prefix('¥')       // 先頭に単位表示
    ->minValue(0)
    ->maxValue(999999),

// 電話番号マスク
TextInput::make('phone')
    ->label('電話番号')
    ->tel()
    ->mask('999-9999-9999'),  // 入力マスク

Select

選択肢から値を選ぶフィールドです。

use Filament\Forms\Components\Select;

// 固定の選択肢
Select::make('status')
    ->label('ステータス')
    ->options([
        'draft'     => '下書き',
        'review'    => 'レビュー中',
        'published' => '公開',
        'archived'  => 'アーカイブ',
    ])
    ->default('draft')
    ->required(),

// 検索可能なセレクト
Select::make('category_id')
    ->label('カテゴリ')
    ->searchable()          // 入力で絞り込み
    ->options(fn () => \App\Models\Category::pluck('name', 'id')->toArray()),

// リレーションから選択
Select::make('category_id')
    ->label('カテゴリ')
    ->relationship('category', 'name')  // hasMany/belongsToリレーション
    ->searchable()
    ->preload(),            // 初期ロード時に全件取得

// 複数選択
Select::make('tag_ids')
    ->label('タグ')
    ->multiple()            // 複数選択を許可
    ->relationship('tags', 'name')
    ->searchable(),

DatePicker / DateTimePicker

use Filament\Forms\Components\DatePicker;
use Filament\Forms\Components\DateTimePicker;

// 日付のみ
DatePicker::make('birth_date')
    ->label('生年月日')
    ->maxDate(now())        // 今日以前のみ選択可能
    ->displayFormat('Y/m/d'),  // 表示フォーマット

// 日時
DateTimePicker::make('published_at')
    ->label('公開日時')
    ->seconds(false)        // 秒を非表示
    ->timezone('Asia/Tokyo')
    ->nullable(),

// 時刻のみ
TimePicker::make('open_time')
    ->label('営業開始時刻')
    ->seconds(false),

FileUpload

ファイルや画像のアップロードフィールドです。

use Filament\Forms\Components\FileUpload;

// 単一画像アップロード
FileUpload::make('thumbnail')
    ->label('サムネイル画像')
    ->image()               // 画像のみ許可
    ->disk('public')        // ストレージディスク
    ->directory('thumbnails')  // 保存ディレクトリ
    ->visibility('public')
    ->imageEditor()         // 画像トリミングUI
    ->maxSize(2048),        // 最大2MB

// 複数ファイルアップロード
FileUpload::make('attachments')
    ->label('添付ファイル')
    ->multiple()
    ->disk('public')
    ->directory('attachments')
    ->acceptedFileTypes(['application/pdf', 'image/*'])
    ->maxFiles(5)
    ->maxSize(10240),       // 最大10MB

config/filesystems.phppublic ディスクを使う場合は、事前にシンボリックリンクを作成しておきます。

php artisan storage:link

RichEditor / MarkdownEditor

リッチテキストエディタです。

use Filament\Forms\Components\RichEditor;
use Filament\Forms\Components\MarkdownEditor;

// リッチテキストエディタ(WYSIWYG)
RichEditor::make('content')
    ->label('本文')
    ->toolbarButtons([
        'attachFiles',
        'blockquote',
        'bold',
        'bulletList',
        'codeBlock',
        'h2',
        'h3',
        'italic',
        'link',
        'orderedList',
        'redo',
        'strike',
        'underline',
        'undo',
    ])
    ->columnSpanFull(),

// Markdownエディタ
MarkdownEditor::make('content')
    ->label('本文(Markdown)')
    ->toolbarButtons([
        'attachFiles',
        'blockquote',
        'bold',
        'bulletList',
        'codeBlock',
        'heading',
        'italic',
        'link',
        'orderedList',
        'redo',
        'strike',
        'table',
        'undo',
    ])
    ->columnSpanFull(),

Repeater

同じフォームを繰り返し追加できるフィールドです。JSONカラムや別テーブルへのサブフォームとして使います。

use Filament\Forms\Components\Repeater;
use Filament\Forms\Components\TextInput;
use Filament\Forms\Components\Select;

// 仕様リスト(JSONカラムに保存)
Repeater::make('specs')
    ->label('仕様')
    ->schema([
        TextInput::make('key')
            ->label('項目名')
            ->required(),
        TextInput::make('value')
            ->label('値')
            ->required(),
    ])
    ->columns(2)            // 2カラムレイアウト
    ->addActionLabel('仕様を追加')
    ->defaultItems(1)       // 初期表示の件数
    ->collapsible(),        // アコーディオン化

// リレーション先のRepeater
Repeater::make('orderItems')
    ->relationship()        // hasMany リレーションを使用
    ->schema([
        Select::make('product_id')
            ->relationship('product', 'name')
            ->required(),
        TextInput::make('quantity')
            ->numeric()
            ->minValue(1)
            ->default(1),
    ]),

バリデーション

Filamentのフォームフィールドには宣言的にバリデーションを追加できます。

基本のバリデーションメソッド

TextInput::make('title')
    ->required()                    // 必須
    ->minLength(3)                  // 最小3文字
    ->maxLength(255)                // 最大255文字
    ->unique(table: 'posts', column: 'title'), // DB uniqueチェック

TextInput::make('price')
    ->numeric()
    ->minValue(0)
    ->maxValue(99999),

Select::make('status')
    ->options([...])
    ->in(['draft', 'published']),   // 選択肢の値に限定

FileUpload::make('avatar')
    ->image()
    ->maxSize(1024),               // 1MB以下

->rules() でLaravelルールを直接指定

TextInput::make('username')
    ->rules([
        'required',
        'alpha_num',               // 英数字のみ
        'min:3',
        'max:20',
        'unique:users,username',
    ]),

// クロージャでカスタムルール
TextInput::make('password_confirm')
    ->rules([
        fn (Forms\Get $get): \Closure => function (string $attribute, $value, \Closure $fail) use ($get) {
            if ($value !== $get('password')) {
                $fail('パスワードが一致しません');
            }
        },
    ]),

->validationMessages() でエラーメッセージをカスタマイズ

TextInput::make('email')
    ->email()
    ->required()
    ->validationMessages([
        'required' => 'メールアドレスは必須です',
        'email'    => '正しいメールアドレス形式で入力してください',
    ]),

フォームの完成例

実際のブログ投稿フォームの完成形です。

public static function form(Form $form): Form
{
    return $form
        ->schema([
            TextInput::make('title')
                ->label('タイトル')
                ->required()
                ->maxLength(255)
                ->columnSpanFull(),

            Select::make('category_id')
                ->label('カテゴリ')
                ->relationship('category', 'name')
                ->searchable()
                ->preload()
                ->required(),

            Select::make('tag_ids')
                ->label('タグ')
                ->multiple()
                ->relationship('tags', 'name')
                ->preload(),

            FileUpload::make('thumbnail')
                ->label('サムネイル')
                ->image()
                ->disk('public')
                ->directory('posts/thumbnails')
                ->columnSpanFull(),

            RichEditor::make('content')
                ->label('本文')
                ->required()
                ->columnSpanFull(),

            Toggle::make('is_published')
                ->label('公開する'),

            DateTimePicker::make('published_at')
                ->label('公開日時')
                ->seconds(false),
        ])
        ->columns(2);  // デフォルトを2カラムに
}

まとめ

  • TextInput はemail/password/numeric/maskなど豊富なオプションを持つ
  • Select はoptions配列・relationship・searchable・multipleに対応
  • DatePicker / DateTimePicker でタイムゾーン対応の日時入力が可能
  • FileUpload でdisk/directory/image/multipleを指定してアップロード
  • RichEditor / MarkdownEditor でリッチテキスト入力
  • Repeater でサブフォームを繰り返し追加できる
  • バリデーションはメソッドチェーンまたは ->rules([]) で追加

次回はテーブルカラムとフィルター機能を深掘りし、一覧画面を強化します。