
Filament 表单验证完全指南字段级校验规则、前端实时反馈与数据库唯一性检查【免费下载链接】filamentA powerful open-source UI framework for Laravel • Build and ship apps admin panels fast with Livewire项目地址: https://gitcode.com/GitHub_Trending/fi/filament表单验证是 FilamentLaravel Livewire 开源 UI 框架构建后台应用时保障数据质量的第一道防线。本文以 packages/forms/docs/23-validation.md 为骨架系统讲解 Filament 表单字段支持的近 50 种内置校验方法、字段间比较、数据库唯一性/存在性校验含scopedUnique/scopedExists的全局作用域处理、自定义规则与错误消息定制并深入CanBeValidatedtrait 源码说明这些声明式 API 最终如何被编译为 Laravel Validator 规则并在前端实时生效。读完本文你将能够在任意 Filament 表单字段上组合出完整、安全、可复用的验证逻辑。一、为什么需要字段级验证在 Laravel 中验证规则通常以数组[required, max:255]或组合字符串required|max:255的形式定义这在后端配合 FormRequest 使用时没有问题。但 Filament 更进一步它能够把验证规则同步到前端让用户在发起任何后端请求之前就能看到错误提示并即时修正提升表单交互体验。Filament 为字段提供了两类能力专用验证方法即下文可用规则一节例如-required()、-email()透传 Laravel 原生规则包括任意 其他 Laravel 验证规则 与 自定义规则。⚠️ 注意某些默认的 Laravel 验证规则依赖正确的 attribute 名称直接通过rule()/rules()传入时可能无法正常工作。只要能用专用验证方法就优先使用专用方法。二、内置可用规则Available Rules所有专用验证方法都定义在 packages/forms/src/Components/Concerns/CanBeValidated.php 的CanBeValidatedtrait 中。以下按用途分组逐一说明语义、参数与代码示例。2.1 基础格式类规则方法校验语义activeUrl()根据 PHPdns_get_record()判断字段必须拥有有效的 A 或 AAAA 记录真实可访问的域名alpha()字段必须全部为字母字符alphaDash()字段可包含字母数字字符以及短横线-和下划线_alphaNum()字段必须全部为字母数字字符ascii()字段必须全部为 7 位 ASCII 字符json()字段必须是合法的 JSON 字符串hexColor()字段必须是合法的十六进制颜色值macAddress()字段必须是合法的 MAC 地址string()字段必须是字符串ulid()字段必须是合法的 ULID通用唯一字典序可排序标识符uuid()字段必须是合法的 RFC 4122版本 1、3、4 或 5UUIDip()/ipv4()/ipv6()字段必须是 IP 地址 / IPv4 / IPv6 地址regex()/notRegex()字段必须匹配 / 不得匹配给定的正则表达式基础格式规则的使用示例Field::make(name)-activeUrl() Field::make(name)-alpha() Field::make(name)-alphaDash() Field::make(name)-alphaNum() Field::make(name)-ascii() Field::make(ip_address)-ip() Field::make(ip_address)-ipv4() Field::make(ip_address)-ipv6() Field::make(ip_address)-json() Field::make(mac_address)-macAddress() Field::make(number)-multipleOf(2) Field::make(email)-regex(/^..$/i) Field::make(email)-notRegex(/^.$/i) Field::make(identifier)-ulid() Field::make(identifier)-uuid() Field::make(color)-hexColor()从源码看这些方法大多是对rule()的薄封装activeUrl()内部执行$this-rule(active_url, $condition)alpha()执行$this-rule(alpha, $condition)以此类推。每个方法都接受一个bool | Closure $condition true参数意味着你可以传闭包实现条件化校验例如仅当满足某条件时才启用该规则。2.2 日期比较类规则方法校验语义after($date)字段值必须是给定日期之后的日期afterOrEqual($date)字段值必须是大于或等于给定日期的日期before($date)字段值必须是给定日期之前的日期beforeOrEqual($date)字段值必须是小于或等于给定日期的日期日期参数支持两类取值字符串日期会被strtotime()解析如tomorrow、first day of next month或另一个字段的名称用于字段间日期比较// 与具体日期比较 Field::make(start_date)-after(tomorrow) Field::make(start_date)-afterOrEqual(tomorrow) Field::make(start_date)-before(first day of next month) Field::make(start_date)-beforeOrEqual(end of this month) // 与另一字段比较 Field::make(start_date) Field::make(end_date)-after(start_date) Field::make(start_date) Field::make(end_date)-afterOrEqual(start_date) Field::make(start_date)-before(end_date) Field::make(end_date) Field::make(start_date)-beforeOrEqual(end_date) Field::make(end_date)源码实现要点dateComparisonRule()会先strtotime($date)判断传入的是否为可解析日期若不是日期且未设置isStatePathAbsolute则通过resolveRelativeStatePath()将其解析为同表单内其他字段的相对 state 路径最终编译成after:end_date这样的 Laravel 规则字符串。2.3 字段间比较类规则方法校验语义confirmed()字段必须存在匹配的{field}_confirmation字段different($field)字段值必须与另一个字段不同same($field)字段值必须与另一个字段相同gt($field)字段值必须大于另一个字段gte($field)字段值必须大于或等于另一个字段lt($field)字段值必须小于另一个字段lte($field)字段值必须小于或等于另一个字段// 密码确认需要一个名为 password_confirmation 的字段 Field::make(password)-confirmed() Field::make(password_confirmation) // 备份邮箱不得与主邮箱相同 Field::make(backup_email)-different(email) // 密码确认 Field::make(password)-same(passwordConfirmation) // 数值大小比较 Field::make(newNumber)-gt(oldNumber) Field::make(newNumber)-gte(oldNumber) Field::make(newNumber)-lt(oldNumber) Field::make(newNumber)-lte(oldNumber)这些方法在源码中统一走fieldComparisonRule()将传入的字段名解析为相对 state 路径后编译成gt:oldNumber形式。比较规则的实现同时支持multiFieldComparisonRule()多字段与multiFieldValueComparisonRule()字段值如required_if其中prohibitedIf、prohibitedUnless、requiredIf、requiredUnless等均属后者并且会自动把BackedEnum转换为-value标量后再拼进规则字符串。2.4 集合与枚举类规则方法校验语义in($values)字段必须包含在给定值列表中notIn($values)字段必须不在给定值列表中enum(EnumClass::class)字段必须是给定枚举类的合法值startsWith($values)字段必须以给定的某个值开头endsWith($values)字段必须以给定的某个值结尾doesntStartWith($values)字段不得以给定的某个值开头doesntEndWith($values)字段不得以给定的某个值结尾Field::make(status)-in([pending, completed]) Field::make(status)-notIn([cancelled, rejected]) Field::make(status)-enum(MyStatus::class) Field::make(name)-startsWith([a]) Field::make(name)-endsWith([bot]) Field::make(name)-doesntStartWith([admin]) Field::make(name)-doesntEndWith([admin])提示toggle buttons、checkbox list、radio 与 select 字段会根据自身可用选项自动应用in()规则无需手动添加。源码中getInValidationRule()会在存在inValidationRuleValues时返回Rule::in($values)否则在设置了枚举时返回Rule::enum($enum)见 CanBeValidated.php。此外mutateStateForValidation()会把枚举对象转为标量值以满足 Laravelin规则对标量入参的要求。2.5 必填、可选与禁止类规则方法校验语义required()字段值不得为空nullable()字段值可以为空未加required时默认如此filled()字段存在时不得为空prohibited()字段值必须为空prohibitedIf($field, $values)仅当另一字段为给定值时本字段必须为空prohibitedUnless($field, $values)除非另一字段为给定值否则本字段必须为空prohibits($fields)若本字段非空则所有其他指定字段必须为空requiredIf($field, $values)仅当另一字段为给定值时本字段不得为空requiredIfAccepted($field)仅当另一字段等于 yes、on、1、1、true 或 true 时本字段不得为空requiredUnless($field, $values)除非另一字段为给定值否则本字段不得为空requiredWith($fields)仅当任一其他指定字段非空时本字段不得为空requiredWithAll($fields)仅当所有其他指定字段均非空时本字段不得为空requiredWithout($fields)仅当任一其他指定字段为空时本字段不得为空requiredWithoutAll($fields)仅当所有其他指定字段均为空时本字段不得为空Field::make(name)-required() Field::make(name)-nullable() Field::make(name)-filled() Field::make(name)-prohibited() Field::make(name)-prohibitedIf(field, value) Field::make(name)-prohibitedUnless(field, value) Field::make(name)-prohibits(field) Field::make(name)-prohibits([field, another_field]) Field::make(name)-requiredIf(field, value) Field::make(name)-requiredIfAccepted(field) Field::make(name)-requiredUnless(field, value) Field::make(name)-requiredWith(field,another_field) Field::make(name)-requiredWithAll(field,another_field) Field::make(name)-requiredWithout(field,another_field) Field::make(name)-requiredWithoutAll(field,another_field)源码细节required()并不直接产生规则字符串而是设置isRequired标志真正编译规则时getRequiredValidationRule()依据isRequired()返回required或nullable——这正是未标记 required 的字段默认可空的底层实现CanBeValidated.php。requiredIf等条件规则由multiFieldValueComparisonRule()编译为required_if:field,value形式支持传BackedEnum值自动取-value。标记字段为必填Marking a field as required默认情况下必填字段的标签旁会显示星号*。在所有字段都必填的表单上你可能想隐藏星号反之对非必填字段也可以手动显示星号来强调。markAsRequired()正是用于控制视觉标记注意它本身不添加任何验证规则use Filament\Forms\Components\TextInput; TextInput::make(name) -required() // 添加必填验证 -markAsRequired(false); // 移除标签旁的星号 // 字段并非 required()但仍想显示星号 TextInput::make(name) -markAsRequired();对应实现位于 packages/forms/src/Components/Concerns/CanBeMarkedAsRequired.phpisMarkedAsRequired()在未显式设置时回退到isRequired()的结果即星号默认跟随必填状态。2.6 数据库存在性校验exists 与 scopedExistsexists()用于校验字段值必须存在于数据库中。默认情况下若表单已关联 Eloquent 模型则直接搜索该模型对应的表关于如何为表单设置模型见 packages/forms/docs/02-form.md你也可以指定自定义的表名或模型、列名use App\Models\Invitation; // 默认使用表单关联模型的表 Field::make(invitation)-exists() // 指定模型/表 Field::make(invitation)-exists(table: Invitation::class) // 指定列 Field::make(invitation)-exists(column: id) // 通过 modifyRuleUsing 进一步定制规则此处为闭包注入 use Illuminate\Validation\Rules\Exists; Field::make(invitation) -exists(modifyRuleUsing: function (Exists $rule) { return $rule-where(is_active, 1); })关键限制务必理解Laravel 原生的exists规则不会通过 Eloquent 模型查询数据库因此不会应用模型上定义的任何全局作用域包括软删除作用域——即使存在同值的软删除记录校验也会通过Filament 的多租户功能同样不会默认将查询限定到当前租户。若希望校验遵守模型的全局作用域包括软删除与多租户请改用scopedExists()——它用基于模型的查询替换 Laravel 原生exists实现use Filament\Forms\Components\TextInput; TextInput::make(email) -scopedExists()如需修改用于存在性检查的 Eloquent 查询例如移除某个全局作用域通过modifyQueryUsing传入函数use Filament\Forms\Components\TextInput; use Illuminate\Database\Eloquent\Builder; use Illuminate\Database\Eloquent\SoftDeletingScope; TextInput::make(email) -scopedExists(modifyQueryUsing: function (Builder $query) { return $query-withoutGlobalScope(SoftDeletingScope::class); })源码印证scopedExists()会基于$component-getModel()发起$model::query()-where($column, $value)查询并默认withoutGlobalScope(SoftDeletingScope::class)再通过modifyQueryUsing钩子允许你移除或追加作用域CanBeValidated.php。失败时使用validation.exists语言包消息可被validationMessages()覆盖。2.7 数据库唯一性校验unique 与 scopedUniqueunique()用于校验字段值在数据库中必须唯一Field::make(email)-unique()如果 Filament 表单已经关联了 Eloquent 模型例如在 panel 资源中见 docs/03-resources/01-overview.mdFilament 会自动使用该模型。也可以显式指定表/模型与列use App\Models\User; Field::make(email)-unique(table: User::class) Field::make(email)-unique(column: email_address)忽略当前记录更新场景的关键在编辑资料表单中包含姓名、邮箱、所在地通常仍要校验邮箱唯一——但如果用户只改了姓名、没动邮箱就不该因为邮箱属于本人而报错。只要表单已关联 Eloquent 模型如 panel 资源Filament 默认就会忽略当前记录。可用参数控制// 禁止 Filament 自动忽略当前 Eloquent 记录 Field::make(email)-unique(ignoreRecord: false) // 指定忽略某条 Eloquent 记录 Field::make(email)-unique(ignorable: $ignoredUser) // 通过 modifyRuleUsing 进一步定制 use Illuminate\Validation\Rules\Unique; Field::make(email) -unique(modifyRuleUsing: function (Unique $rule) { return $rule-where(is_active, 1); })源码中ignoreRecord的默认值来自shouldUniqueValidationIgnoreRecordByDefault()默认true配合ignorable时通过Rule::unique(...)-ignore($record-getOriginal($key), $record-getQualifiedKeyName())构造忽略逻辑CanBeValidated.php。同样的全局作用域限制Laravel 原生unique规则不会经过 Eloquent 模型因此不会应用全局作用域含软删除与多租户。这可能导致同值软删除记录也会导致校验失败。解决方案是scopedUnique()use Filament\Forms\Components\TextInput; TextInput::make(email) -scopedUnique()同样支持忽略当前记录与修改查询use Filament\Forms\Components\TextInput; use Illuminate\Database\Eloquent\Builder; use Illuminate\Database\Eloquent\SoftDeletingScope; TextInput::make(email) -scopedUnique(modifyQueryUsing: function (Builder $query) { return $query-withoutGlobalScope(SoftDeletingScope::class); })从源码看scopedUnique()基于模型查询$model::query()-where($column, $value)并通过whereKeyNot($ignorable)排除当前记录然后交给modifyQueryUsing进一步调整CanBeValidated.php。三、其他 Laravel 规则rules() 方法任何 Laravel 原生验证规则都可以通过rules()方法附加到字段上支持数组或|分隔的字符串两种形式TextInput::make(slug)-rules([alpha_dash]) // 等价写法 TextInput::make(slug)-rules(alpha_dash|max:255)从源码看rules()内部会将字符串按|拆分为数组每条规则连同条件一起存入$this-rules它还接受闭包作为规则来源以及bool | Closure $condition条件参数实现规则的条件启停CanBeValidated.php。完整规则清单可查阅 Laravel 官方验证文档。四、自定义规则Custom RulesFilament 完全兼容 Laravel 的自定义验证规则// 规则类对象 TextInput::make(slug)-rules([new Uppercase()]) // 闭包规则 use Closure; TextInput::make(slug)-rules([ fn (): Closure function (string $attribute, $value, Closure $fail) { if ($value foo) { $fail(The :attribute is invalid.); } }, ])在自定义规则中注入其他字段状态如果你的自定义规则需要引用表单中其他字段的值可以利用 Filament 的字段工具注入utility injection见 packages/forms/docs/01-overview.md把$get等工具注入到闭包规则中。方法是将闭包规则再包一层函数use Filament\Schemas\Components\Utilities\Get; TextInput::make(slug)-rules([ fn (Get $get): Closure function (string $attribute, $value, Closure $fail) use ($get) { if ($get(other_field) foo $value ! bar) { $fail(The {$attribute} is invalid.); } }, ])外层函数让 Filament 得以解析并注入Get工具内层闭包再通过use ($get)捕获它——这是引用同表单其他字段状态的标准模式。五、自定义校验属性名validationAttribute字段校验失败时错误消息中使用的属性名默认取自字段的 label。可用validationAttribute()自定义use Filament\Forms\Components\TextInput; TextInput::make(name) -validationAttribute(full name)除了静态字符串该方法也接受一个函数来动态计算属性名可注入各种工具。源码中getValidationAttribute()在显式设置时返回其求值结果否则回退到Str::lcfirst($label)即标签首字母小写CanBeValidated.php。六、定制验证错误消息validationMessages默认使用 Laravel 的标准错误消息。通过validationMessages()按规则名覆盖use Filament\Forms\Components\TextInput; TextInput::make(email) -unique(/* ... */) -validationMessages([ unique The :attribute has already been registered., ])每条消息既可以写静态字符串也可以用函数动态计算可注入工具。消息在getValidationMessages()中被逐条求值CanBeValidated.php并最终通过dehydrateValidationMessages()以{statePath}.{rule}为键合入整表单的消息表。允许在验证消息中渲染 HTML出于 XSS 防护验证消息默认以纯文本渲染。某些场景如展示列表或链接需要渲染 HTML可显式开启use Filament\Forms\Components\TextInput; TextInput::make(password) -required() -rules([ new CustomRule(), // 返回包含 HTML 的验证消息的自定义规则 ]) -allowHtmlValidationMessages()⚠️ 危险操作提示开启该选项等于对该字段的验证消息放弃转义。请确保每一条消息——包括来自自定义规则或翻译文件的——都足够安全。不可信内容可能导致 XSS。源码中allowHtmlValidationMessages()的注释也明确记录了这一点CanBeValidated.php。七、禁用未保存字段的校验validatedWhenNotDehydrated默认情况下即使字段设置了不保存saved(false)/dehydrated(false)见 packages/forms/docs/01-overview.md它仍然参与校验。如果希望未保存的字段不再校验使用validatedWhenNotDehydrated(false)use Filament\Forms\Components\TextInput; TextInput::make(name) -required() -saved(false) -validatedWhenNotDehydrated(false)该方法同样支持传函数动态计算。对应实现位于 packages/schemas/src/Components/Concerns/HasState.php而规则收集阶段的过滤逻辑在 Schema 层getValidationRules()会跳过isNeitherDehydratedNorValidated()的组件packages/schemas/src/Concerns/CanBeValidated.php。八、验证规则的底层流水线从字段到 Validator了解规则如何从字段方法变成 Laravel Validator 的输入有助于调试复杂表单收集Schema 的getValidationRules()遍历所有组件含隐藏组件对每个实现HasValidationRules契约的组件调用dehydrateValidationRules($rules)以state 路径为键写入规则表CanBeValidated.php。编译字段的getValidationRules()将必填/可空规则、长度规则若字段实现CanBeLengthConstrained、in/enum规则、正则规则以及所有通过rule()/rules()追加的规则汇总为数组CanBeValidated.php。条件为闭包时逐条求值不满足条件的规则被跳过。执行Schema 的validate()最终调用$livewire-validate($rules, $messages, $attributes)packages/schemas/src/Concerns/CanBeValidated.php走 Laravel 标准验证流程——同时为 Livewire 前端实时校验与后端提交校验复用同一套规则。测试佐证仓库测试 tests/src/Forms/ValidationTest.php 覆盖了必填规则触发-required()产生Required失败键、自定义规则透传-rule(email)、条件校验-required($bool)的随机开关以及未脱水字段默认仍校验 / 配置后不再校验等行为可作为自定义验证逻辑的行为参照。九、实战建议小结优先专用方法-required()、-email()这类方法语义清晰、自动处理 attribute 与 state 路径尽量避免手写rules([required, ...])。记住默认可空未加required的字段默认注入nullable空值不会触发格式类规则报错。区分原生与 scoped 数据库规则涉及软删除模型或多租户表单时使用scopedUnique()/scopedExists()才能让全局作用域生效。更新表单务必忽略自身记录unique默认忽略当前关联记录跨场景显式传ignorable或ignoreRecord更稳妥。HTML 消息默认关闭除非消息内容完全可信否则不要开启allowHtmlValidationMessages()。未保存字段默认仍校验需要跳过时显式validatedWhenNotDehydrated(false)。通过组合上述规则、字段工具注入与条件闭包你可以为任何 Filament 表单构建从客户端实时反馈到服务端兜底的完整验证体系。【免费下载链接】filamentA powerful open-source UI framework for Laravel • Build and ship apps admin panels fast with Livewire项目地址: https://gitcode.com/GitHub_Trending/fi/filament创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考