forms について
FilamentPHP Forms Generation Skill は、Filament アプリケーションを開発する人向けに FilamentPHP v4 のフォームスキーマを生成します。Section または Fieldset で項目を整理し、バリデーション、リレーション、条件分岐にも対応します。
主な機能
- 01FilamentPHP v4 のフォームスキーマ生成
- 02項目とバリデーション規則の設定
- 03モデルリレーションの処理
- 04レイアウトコンポーネントで項目を整理
- 05条件付き表示の設定
活用シーン
- 01項目を分類したフォームの作成
- 02項目へのバリデーション規則の追加
- 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
]);
}
スキーマの構成要件
重要: すべてのフォームスキーマは、レイアウトコンポーネントを使って構成する必要があります。フォームスキーマのルートレベルにフィールドを直接配置してはいけません。
最低限の構成ルール
- 必ず Section または Fieldset を使用する - すべてのフォームで、少なくとも 1 つの Section または Fieldset でフィールドを囲む必要があります
- 関連するフィールドをまとめる - 論理的に関連するフィールドは、同じ Section または Fieldset にまとめます
- わかりやすいラベルを使用する - Section と Fieldset には、内容を適切に表すタイトルを付けます
- 折りたたみ可能にすることを検討する - フォームが長い場合は、セクションを折りたたみ可能にします
推奨する階層構造
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 | 説明やアイコンを設定でき、折りたたみ可能な大きな論理グループ |
| Fieldset | Section 内の小さなグループ。視覚的な強調は控えめです |
| Tabs | セクションが多く、スクロールが必要になる場合 |
| Grid | Section 内の列レイアウト(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');
出力
生成されるフォームには、以下が含まれます。
- 適切なインポート(必要に応じて Section、Fieldset を含む)
- 型宣言
- Section/Fieldset で構成されたスキーマ(必須)
- バリデーションルール
- 必要に応じた列を含むレイアウト構造
- リレーションシップの処理
- 条件付きロジック
- 任意または二次的な内容の折りたたみ可能なセクション
コメント
Markdown 対応