mcpField
F
forms

forms

github.com/mwguerra開発3695 回インストール27 週間前に更新
リポジトリ

forms について

FilamentPHP Forms Generation Skill は、Filament アプリケーションを開発する人向けに FilamentPHP v4 のフォームスキーマを生成します。Section または Fieldset で項目を整理し、バリデーション、リレーション、条件分岐にも対応します。

主な機能

  1. 01FilamentPHP v4 のフォームスキーマ生成
  2. 02項目とバリデーション規則の設定
  3. 03モデルリレーションの処理
  4. 04レイアウトコンポーネントで項目を整理
  5. 05条件付き表示の設定

活用シーン

  1. 01項目を分類したフォームの作成
  2. 02項目へのバリデーション規則の追加
  3. 03リレーション項目を含むフォームの生成

インストール

コマンドをコピーしてターミナルで実行します。

$npx skills add https://github.com/mwguerra/claude-code-plugins --skill forms

インストール後、Claude Code で/formsを入力して読み込まれたか確認します。

詳細

FilamentPHP フォーム生成スキル

概要

このスキルは、適切なフィールド設定、バリデーションルール、リレーションシップ、レイアウトコンポーネントを備えた FilamentPHP v4 のフォームスキーマを生成します。

ドキュメントの参照先

重要: フォームを生成する前に、以下を読んでください。

  • /home/mwguerra/projects/mwguerra/claude-code-plugins/filament-specialist/skills/docs/references/forms/
  • /home/mwguerra/projects/mwguerra/claude-code-plugins/filament-specialist/skills/docs/references/schemas/

ワークフロー

ステップ 1:要件を分析する

以下を特定します。

  • 必要なフィールドの種類
  • バリデーションルール
  • リレーションシップ(belongsTo、hasMany など)
  • レイアウトの設定(セクション、タブ、列)
  • 条件付き表示
  • カスタム書式設定
ステップ 2:ドキュメントを読む

フォームのドキュメントを参照し、以下を確認します。

  • 正確なフィールドクラス名
  • 利用可能なメソッドとオプション
  • バリデーション連携のパターン
  • リレーションシップの処理方法
ステップ 3:スキーマを生成する

適切な構造でフォームスキーマを構築します。

use Filament\Forms;
use Filament\Forms\Form;
use Filament\Forms\Components\Section;
use Filament\Forms\Components\Fieldset;
use Filament\Forms\Components\Grid;
use Filament\Forms\Components\TextInput;
use Filament\Forms\Components\Select;

public static function form(Form $form): Form
{
    return $form
        ->schema([
            // Fields organized in sections/fieldsets
        ]);
}

スキーマの構成要件

重要: すべてのフォームスキーマは、レイアウトコンポーネントを使って構成する必要があります。フォームスキーマのルートレベルにフィールドを直接配置してはいけません。

最低限の構成ルール
  1. 必ず Section または Fieldset を使用する - すべてのフォームで、少なくとも 1 つの Section または Fieldset でフィールドを囲む必要があります
  2. 関連するフィールドをまとめる - 論理的に関連するフィールドは、同じ Section または Fieldset にまとめます
  3. わかりやすいラベルを使用する - Section と Fieldset には、内容を適切に表すタイトルを付けます
  4. 折りたたみ可能にすることを検討する - フォームが長い場合は、セクションを折りたたみ可能にします
推奨する階層構造
Form Schema
├── Section: "Primary Information"
│   ├── Fieldset: "Basic Details" (optional grouping)
│   │   ├── TextInput: name
│   │   └── TextInput: email
│   └── Fieldset: "Contact" (optional grouping)
│       ├── TextInput: phone
│       └── TextInput: address
├── Section: "Settings"
│   ├── Toggle: is_active
│   └── Select: status
└── Section: "Media" (collapsible)
    └── FileUpload: avatar
悪い例(このようにしないでください)
// ❌ WRONG: Fields at root level without organization
public static function form(Form $form): Form
{
    return $form
        ->schema([
            TextInput::make('name'),
            TextInput::make('email'),
            TextInput::make('phone'),
            Toggle::make('is_active'),
            FileUpload::make('avatar'),
        ]);
}
良い例(必ずこのようにしてください)
// ✅ CORRECT: Fields organized in sections
public static function form(Form $form): Form
{
    return $form
        ->schema([
            Section::make('Personal Information')
                ->description('Basic user details')
                ->schema([
                    TextInput::make('name')
                        ->required()
                        ->maxLength(255),
                    TextInput::make('email')
                        ->email()
                        ->required(),
                    TextInput::make('phone')
                        ->tel(),
                ]),

            Section::make('Settings')
                ->schema([
                    Toggle::make('is_active')
                        ->label('Active')
                        ->default(true),
                ]),

            Section::make('Profile Image')
                ->collapsible()
                ->schema([
                    FileUpload::make('avatar')
                        ->image()
                        ->disk('public')
                        ->directory('avatars'),
                ]),
        ]);
}
Section と Fieldset の使い分け
コンポーネント使用場面
Section説明やアイコンを設定でき、折りたたみ可能な大きな論理グループ
FieldsetSection 内の小さなグループ。視覚的な強調は控えめです
Tabsセクションが多く、スクロールが必要になる場合
GridSection 内の列レイアウト(Section の代わりにはなりません)
複雑なフォームの例
public static function form(Form $form): Form
{
    return $form
        ->schema([
            Section::make('Product Details')
                ->icon('heroicon-o-cube')
                ->schema([
                    Fieldset::make('Basic Information')
                        ->schema([
                            TextInput::make('name')
                                ->required()
                                ->maxLength(255),
                            TextInput::make('sku')
                                ->required()
                                ->unique(ignoreRecord: true),
                        ])
                        ->columns(2),

                    Fieldset::make('Pricing')
                        ->schema([
                            TextInput::make('price')
                                ->numeric()
                                ->prefix('$')
                                ->required(),
                            TextInput::make('compare_at_price')
                                ->numeric()
                                ->prefix('$'),
                        ])
                        ->columns(2),

                    RichEditor::make('description')
                        ->columnSpanFull(),
                ]),

            Section::make('Inventory')
                ->icon('heroicon-o-archive-box')
                ->collapsible()
                ->schema([
                    TextInput::make('quantity')
                        ->numeric()
                        ->default(0),
                    Toggle::make('track_inventory')
                        ->default(true),
                ]),

            Section::make('Media')
                ->icon('heroicon-o-photo')
                ->collapsible()
                ->collapsed()
                ->schema([
                    FileUpload::make('images')
                        ->multiple()
                        ->image()
                        ->reorderable(),
                ]),
        ]);
}

フィールドの完全なリファレンス

テキスト入力フィールド
// Basic text input
TextInput::make('name')
    ->required()
    ->maxLength(255)
    ->placeholder('Enter name...')
    ->helperText('This will be displayed publicly')
    ->prefixIcon('heroicon-o-user');

// Email input
TextInput::make('email')
    ->email()
    ->required()
    ->unique(ignoreRecord: true);

// Password input
TextInput::make('password')
    ->password()
    ->required()
    ->confirmed()
    ->minLength(8);

// Numeric input
TextInput::make('price')
    ->numeric()
    ->prefix('$')
    ->minValue(0)
    ->maxValue(10000)
    ->step(0.01);

// Phone input
TextInput::make('phone')
    ->tel()
    ->telRegex('/^[+]*[(]{0,1}[0-9]{1,4}[)]{0,1}[-\s\.\/0-9]*$/');

// URL input
TextInput::make('website')
    ->url()
    ->suffixIcon('heroicon-o-globe-alt');
テキストエリアフィールド
// Basic textarea
Textarea::make('description')
    ->rows(5)
    ->cols(20)
    ->minLength(10)
    ->maxLength(1000)
    ->columnSpanFull();

// Auto-resize textarea
Textarea::make('content')
    ->autosize()
    ->columnSpanFull();
リッチテキストエディター
// Rich editor
RichEditor::make('content')
    ->toolbarButtons([
        'blockquote',
        'bold',
        'bulletList',
        'codeBlock',
        'h2',
        'h3',
        'italic',
        'link',
        'orderedList',
        'redo',
        'strike',
        'underline',
        'undo',
    ])
    ->columnSpanFull();

// Markdown editor
MarkdownEditor::make('content')
    ->toolbarButtons([
        'bold',
        'bulletList',
        'codeBlock',
        'edit',
        'italic',
        'link',
        'orderedList',
        'preview',
        'strike',
    ])
    ->columnSpanFull();
選択フィールド
// Basic select
Select::make('status')
    ->options([
        'draft' => 'Draft',
        'reviewing' => 'Reviewing',
        'published' => 'Published',
    ])
    ->default('draft')
    ->required();

// Searchable select
Select::make('country')
    ->options(Country::pluck('name', 'id'))
    ->searchable()
    ->preload();

// Multiple select
Select::make('tags')
    ->multiple()
    ->options(Tag::pluck('name', 'id'))
    ->searchable();

// BelongsTo relationship
Select::make('author_id')
    ->relationship('author', 'name')
    ->searchable()
    ->preload()
    ->createOptionForm([
        TextInput::make('name')
            ->required(),
        TextInput::make('email')
            ->email()
            ->required(),
    ]);

// BelongsToMany relationship
Select::make('categories')
    ->relationship('categories', 'name')
    ->multiple()
    ->preload();
ブール値フィールド
// Toggle switch
Toggle::make('is_active')
    ->label('Active')
    ->default(true)
    ->onColor('success')
    ->offColor('danger');

// Checkbox
Checkbox::make('terms_accepted')
    ->label('I accept the terms and conditions')
    ->required()
    ->accepted();

// Checkbox list
CheckboxList::make('permissions')
    ->options([
        'create' => 'Create',
        'read' => 'Read',
        'update' => 'Update',
        'delete' => 'Delete',
    ])
    ->columns(2);

// Radio buttons
Radio::make('plan')
    ->options([
        'basic' => 'Basic Plan',
        'pro' => 'Pro Plan',
        'enterprise' => 'Enterprise Plan',
    ])
    ->descriptions([
        'basic' => 'Best for individuals',
        'pro' => 'Best for small teams',
        'enterprise' => 'Best for large organizations',
    ])
    ->required();
日付・時刻フィールド
// Date picker
DatePicker::make('birth_date')
    ->native(false)
    ->displayFormat('d/m/Y')
    ->maxDate(now())
    ->closeOnDateSelection();

// DateTime picker
DateTimePicker::make('published_at')
    ->native(false)
    ->displayFormat('d/m/Y H:i')
    ->seconds(false)
    ->timezone('America/New_York');

// Time picker
TimePicker::make('start_time')
    ->native(false)
    ->seconds(false)
    ->minutesStep(15);
ファイルアップロードフィールド
// Basic file upload
FileUpload::make('attachment')
    ->disk('public')
    ->directory('attachments')
    ->acceptedFileTypes(['application/pdf', 'image/*'])
    ->maxSize(10240)
    ->downloadable()
    ->openable();

// Image upload with preview
FileUpload::make('avatar')
    ->image()
    ->imageEditor()
    ->circleCropper()
    ->disk('public')
    ->directory('avatars')
    ->visibility('public');

// Multiple files
FileUpload::make('gallery')
    ->multiple()
    ->reorderable()
    ->appendFiles()
    ->image()
    ->disk('public')
    ->directory('gallery');

// Spatie Media Library
SpatieMediaLibraryFileUpload::make('images')
    ->collection('images')
    ->multiple()
    ->reorderable();
複合フィールド
// Repeater (HasMany inline editing)
Repeater::make('items')
    ->relationship()
    ->schema([
        TextInput::make('name')
            ->required(),
        TextInput::make('quantity')
            ->numeric()
            ->required(),
        TextInput::make('price')
            ->numeric()
            ->prefix('$'),
    ])
    ->columns(3)
    ->defaultItems(1)
    ->addActionLabel('Add Item')
    ->reorderable()
    ->collapsible();

// Builder (flexible content)
Builder::make('content')
    ->blocks([
        Builder\Block::make('heading')
            ->schema([
                TextInput::make('content')
                    ->label('Heading')
                    ->required(),
                Select::make('level')
                    ->options([
                        'h2' => 'H2',
                        'h3' => 'H3',
                        'h4' => 'H4',
                    ]),
            ]),
        Builder\Block::make('paragraph')
            ->schema([
                RichEditor::make('content')
                    ->required(),
            ]),
        Builder\Block::make('image')
            ->schema([
                FileUpload::make('url')
                    ->image()
                    ->required(),
                TextInput::make('alt')
                    ->label('Alt text'),
            ]),
    ])
    ->columnSpanFull();

// Key-Value pairs
KeyValue::make('metadata')
    ->keyLabel('Property')
    ->valueLabel('Value')
    ->addActionLabel('Add Property')
    ->reorderable();

// Tags input
TagsInput::make('tags')
    ->suggestions([
        'laravel',
        'filament',
        'php',
    ])
    ->splitKeys(['Tab', ',']);
非表示フィールドと特殊フィールド
// Hidden field
Hidden::make('user_id')
    ->default(auth()->id());

// Placeholder (display only)
Placeholder::make('created_at')
    ->label('Created')
    ->content(fn ($record): string => $record?->created_at?->diffForHumans() ?? '-');

// View field (custom blade view)
View::make('custom-field')
    ->view('filament.forms.custom-field');

レイアウトコンポーネント

Section
Section::make('Personal Information')
    ->description('Enter your personal details')
    ->icon('heroicon-o-user')
    ->collapsible()
    ->collapsed(false)
    ->schema([
        // Fields
    ]);
Fieldset
Fieldset::make('Address')
    ->schema([
        TextInput::make('street'),
        TextInput::make('city'),
        TextInput::make('state'),
        TextInput::make('zip'),
    ])
    ->columns(2);
Tabs
Tabs::make('Tabs')
    ->tabs([
        Tabs\Tab::make('General')
            ->icon('heroicon-o-information-circle')
            ->schema([
                // General fields
            ]),
        Tabs\Tab::make('Media')
            ->icon('heroicon-o-photo')
            ->schema([
                // Media fields
            ]),
        Tabs\Tab::make('SEO')
            ->icon('heroicon-o-magnifying-glass')
            ->schema([
                // SEO fields
            ]),
    ])
    ->columnSpanFull();
Grid と列
Grid::make()
    ->schema([
        TextInput::make('first_name')
            ->columnSpan(1),
        TextInput::make('last_name')
            ->columnSpan(1),
        TextInput::make('email')
            ->columnSpanFull(),
    ])
    ->columns(2);
分割レイアウト
Split::make([
    Section::make('Main Content')
        ->schema([
            // Primary fields
        ]),
    Section::make('Sidebar')
        ->schema([
            // Secondary fields
        ])
        ->grow(false),
]);

バリデーション

TextInput::make('email')
    ->email()
    ->required()
    ->unique(table: User::class, ignoreRecord: true)
    ->rules(['required', 'email', 'max:255']);

TextInput::make('slug')
    ->required()
    ->unique(ignoreRecord: true)
    ->rules([
        fn (): Closure => function (string $attribute, $value, Closure $fail) {
            if (str_contains($value, ' ')) {
                $fail('Slug cannot contain spaces.');
            }
        },
    ]);

条件付き表示

Select::make('type')
    ->options([
        'individual' => 'Individual',
        'company' => 'Company',
    ])
    ->live();

TextInput::make('company_name')
    ->visible(fn (Get $get): bool => $get('type') === 'company');

TextInput::make('tax_id')
    ->hidden(fn (Get $get): bool => $get('type') !== 'company');

出力

生成されるフォームには、以下が含まれます。

  1. 適切なインポート(必要に応じて Section、Fieldset を含む)
  2. 型宣言
  3. Section/Fieldset で構成されたスキーマ(必須)
  4. バリデーションルール
  5. 必要に応じた列を含むレイアウト構造
  6. リレーションシップの処理
  7. 条件付きロジック
  8. 任意または二次的な内容の折りたたみ可能なセクション

コメント