关于 forms
FilamentPHP Forms Generation Skill 用于生成 FilamentPHP v4 表单 schema,面向开发 Filament 应用的开发者。它要求使用 Section 或 Fieldset 组织字段,并涵盖验证、关系处理和条件逻辑。
主要功能
- 01生成 FilamentPHP v4 表单 schema
- 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 - 每个表单都必须至少有一个 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 | 分区内较小的子分组,视觉权重较轻 |
| Tabs | 分区较多、会导致页面滚动时使用 |
| Grid | 分区内的分列布局(不能替代分区) |
复杂表单示例
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