steven-fox / laravel-model-validation
模型验证的救星。
Requires
- php: ^8.2
- illuminate/collections: ^10.0||^11.0
- illuminate/contracts: ^10.0||^11.0
- illuminate/database: ^10.0||^11.0
- illuminate/events: ^10.0||^11.0
- illuminate/support: ^10.0||^11.0
- illuminate/validation: ^10.0||^11.0
- spatie/laravel-package-tools: ^1.16
Requires (Dev)
- larastan/larastan: ^2.9
- laravel/pint: ^1.14
- nunomaduro/collision: ^8.1.1||^7.10.0
- orchestra/testbench: ^9.0.0||^8.22.0
- pestphp/pest: ^2.34
- pestphp/pest-plugin-arch: ^2.7
- pestphp/pest-plugin-laravel: ^2.3
- phpstan/extension-installer: ^1.3
- phpstan/phpstan-deprecation-rules: ^1.1
- phpstan/phpstan-phpunit: ^1.3
README
此包使您能够轻松地为Eloquent模型添加验证助手。虽然已经存在一些类似的包,但此包旨在通过可定制的Laravel风格实现无懈可击的功能。
此包的目的在于
- 通过提供一个单点位置来定义模型的基线验证配置,以减少代码重复。
- 轻松检索该配置以补充特定的验证场景(如FormRequests、管理表单/操作、控制台输入等)。
- 通过确保模型遵守一组特定的规则(独立于UI)来为应用程序提供数据完整性。
主要功能
- 为您的模型添加了多个验证方法,如
validate()
、passesValidation()
和failsValidation()
。 - 可以将验证配置为在保存模型时自动发生(通过接口进行选择,在需要时可以全局关闭)。
- 验证规则可以配置为单个定义,也可以根据创建和更新拆分为独立的规则。
- 可以在运行时覆盖或与自定义规则混合验证规则。
- 可以在验证之前自定义和转换用于验证的数据。
- 模型可以定义自定义验证消息和属性名称。
- 验证配置(数据、规则、消息、名称)可以通过公共方法访问,因此很容易将它们集成到验证过程中,例如请求、控制器、Nova、Filament等。
- 提供了用于
validating
和validated
的模型事件挂钩,易于使用,并且可以用于现有的模型观察者。 - 可以为特定模型事件定义自定义验证监听器。
- 提供了用于与需要忽略当前模型记录以进行更新的
Unique
规则一起工作的助手。 - 抛出一个包含已验证模型的属性的自定义ValidationException,以帮助调试、记录和错误消息。
安装
您可以通过composer安装此包
composer require steven-fox/laravel-model-validation
用法
ValidatesAttributes
特性
通过以下方式将验证功能添加到模型中:
- 将
StevenFox\LaravelModelValidation\ValidatesAttributes
特性添加到模型中。 - 通过以下方法之一在模型上定义规则:
baseValidationRules()
、validationRulesUniqueToCreating()
、validationRulesUniqueToUpdating()
、validationRulesForCreating()
、validationRulesForUpdating()
。
use Illuminate\Database\Eloquent\Model; use StevenFox\LaravelModelValidation\ValidatesAttributes; class ValidatingModel extends Model { use ValidatesAttributes; protected function baseValidationRules(): array { return [ // rules go here as ['attribute' => ['rule1', 'rule2', ...] // like a normal validation setup ]; } // Other methods are available for more control over rules... see below. } $model = new ValidatingModel($request->json()); $model->validate(); // A ModelValidationException is thrown if validation fails. $model->save(); // Other helpful methods... $passes = $model->passesValidation(); // An exception, will *not* be thrown if validation fails. $fails = $model->failsValidation(); // No exception thrown here, either. $validator = $model->validator();
ValidatesWhenSaving
接口
您可以通过添加\StevenFox\LaravelModelValiation\Contracts\ValidatesWhenSaving
接口使模型在保存时自动执行验证。这是一个可选功能。如果您未在单个模型上实现此接口,您仍然可以在命令中执行验证;它只是不会在save()
过程中自动执行。
use Illuminate\Database\Eloquent\Model; use StevenFox\LaravelModelValidation\Contracts\ValidatesWhenSaving; use StevenFox\LaravelModelValidation\ValidatesAttributes; class ValidatingModel extends Model implements ValidatesWhenSaving { use ValidatesAttributes; // This model will now validate upon saving. }
验证监听器
默认情况下,此包将注册一个事件监听器,用于在保存模型之前执行验证,该监听器针对creating
和updating
模型事件。您可以通过在模型上覆盖静态validatingListeners()
方法来自定义此行为。以下是默认实现,您可以调整以满足您的需求。
protected static function validatingListeners(): array { return [ 'creating' => ValidateModel::class, 'updating' => ValidateModel::class, ]; }
注意:我们特别使用了
creating
和updating
事件,而不是更通用的saving
事件,这样我们就不需要重复验证一个“保存”而没有更改属性的模型(这不会触发updating
事件,从而避免了冗余)。
注意:自动验证过程是通过Laravel的模型事件系统实现的。因此,如果您执行了
saveQuietly()
或其他禁用/中断模型事件链的操作,将自动禁用验证。
更精细的验证规则控制
您可以使用以下方法来更精细地控制特定情况下使用的验证规则。
use Illuminate\Database\Eloquent\Model; use StevenFox\LaravelModelValidation\ValidatesAttributes; class ValidatingModel extends Model { use ValidatesAttributes; /** * Define rules that are common to both creating and updating a model record. */ protected function baseValidationRules(): array { return [ 'name' => ['required', 'string', 'max:255'], // more rules... ]; } /** * Define the rules that are unique to creating a record. * These rules will be *combined* with the common validation rules. * */ protected function validationRulesUniqueToCreating(): array { return ['a_unique_column' => Rule::unique('table')]; } /** * Define the rules that are unique to updating a record. * These rules will be combined with the common validation rules. * */ protected function validationRulesUniqueToUpdating(): array { return ['a_unique_column' => Rule::unique('table')->ignoreModel($this)]; } /** * Define the rules that are used when creating a record. * If you overload this method on your model, the 'baseValidationRules' * will not be used by default. */ protected function validationRulesForCreating(): array { // ... } /** * Define the rules that are used when updating a record. * If you overload this method on your model, the 'baseValidationRules' * will not be used by default. */ protected function validationRulesForUpdating(): array { // ... } }
唯一列
将属性指定为unique
是一个常见的验证需求。因此,此包提供了一个快捷方式,您可以在baseValidationRules()
方法中使用它来为唯一列。辅助函数将为属性简单地定义一个Unique
规则,并且当模型记录已存在于数据库中时,该规则将自动调用ignoreModel($this)
方法。
/** * Define rules that are common to both creating and updating a model record. */ protected function baseValidationRules(): array { return [ 'email' => [ 'required', 'email', 'max:255', $this->uniqueRule(), // adds a unique rule that handles ignoring the current record on update ], // more rules... ]; }
运行时自定义规则
替代规则
您可以使用setSupersedingValidationRules()
方法设置临时规则,这些规则将替换模型上定义的所有其他规则。
$model = new ValidatingModel(); $customRules = [ // ... ]; $model->setSupersedingValidationRules($customRules); $model->validate(); // The validator will **only** use the $customRules for validation. $model->clearSupersedingValidationRules(); $model->validate(); // The validator will now go back to using the normal rules defined on the model.
注意:您可以将特定模型实例的验证临时禁用,方法是将
supersedingValidationRules
设置为空数组。验证过程仍然会运行,但没有规则进行验证,模型将自动通过。
$model = new ValidatingModel(); $model->setSupersedingValidationRules([]); $model->validate(); // Validation will run, but with no rules defined, no actual validation will occur. $model->clearSupersedingValidationRules(); $model->validate(); // Validation will occur normally.
混合规则
您可以使用addMixinValidationRules()
和setMixinValidationRules()
方法定义将与其他模型上定义的规则合并的规则。您混合到特定属性的规则将替换该属性上的现有规则。
例如,假设您的模型指定默认情况下dateTime列必须简单地是date
,但在特定情况下,您想确保该属性的值是在特定时刻之后的日期。您可以通过在运行时为该属性混合此自定义规则集来执行此操作。
$model = new ValidatingModel(); // Normally, the ValidatingModel specifies that the 'date_attribute' // is simply a 'date'. // However, here we will specify that it must be a date after tomorrow. $mixinRules = [ 'date_attribute' => ['date', 'after:tomorrow'] ]; $model->addMixinValidationRules($mixinRules); $model->validate(); // The validator will use a *combination* of the mixin rules and the standard rules defined within the model. $model->clearMixinValidationRules(); $model->validate(); // The validator will now go back to using the normal rules defined on the model.
验证数据
默认情况下,此包将使用模型的getAttributes()
方法作为传递给验证器实例的数据。通常,从getAttributes()
方法返回的数组表示将存储在数据库中的原始值。这意味着具有铸造的属性将转换为存储使用的格式,使验证逻辑尽可能无缝。例如,大多数模型上的日期属性都转换为Carbon
实例,但在验证日期时,验证器需要接收日期的字符串表示,而不是Carbon
实例。
如果您需要自定义用于验证的数据属性,您可以通过以下两种方式之一进行操作
- 重载
rawAttributesForValidation()
方法并返回所需的内容。 - 重载
prepareAttributesForValidation($attributes)
方法将默认属性值转换为验证就绪状态。
访问验证配置
您可以使用以下方法访问模型的验证规则、数据、消息和属性名称。
$model = new ValidatingModel(); // --- Rules --- // Retrieve all rules with $allRules = $model->validationRules(); // or, retrieve rules for certain attributes... $specificRules = $model->validationRules('attr_1', 'attr_2', ...); // --- Data --- // Retrieve all validation data with $allData = $model->validationData(); // or, retrieve the data for certain attributes... $specificData = $model->validationData('attr_1', 'attr_2', ...); // --- Custom Messages and Attribute Names --- $customMessages = $model->customValidationMessages(); $customAttributeNames = $model->customValidationAttributeNames();
保存时全局禁用验证
对于实现ValidatesOnSave
接口的模型,在保存过程中可以禁用自动验证。这有助于设置特定的测试,例如。
选项1
在验证模型类上调用静态disableValidationWhenSaving()
。这将禁用验证,直到您显式再次激活它。这与Model::unguard()
概念类似,并且像unguard一样,您可能会在ServiceProvider
的boot
方法中禁用验证。
// Perhaps in a ServiceProvider... public function boot(): void { ValidatingModel::disableValidationWhenSaving(); // All models that validate their attributes and implement // the ValidatesWhenSaving interface will no longer perform // that automatic validation during the saving process. }
选项2
调用静态whileValidationDisables()
方法,传入一个回调,该回调将执行在全局禁用自动验证时希望执行的逻辑。这与Model::unguarded($callback)
概念类似。
ValidatingModel::whileValidationDisabled(function () { // do something while automatic validation is globally disabled... });
验证模型事件
此包添加了validating
和validated
模型事件。它还注册了这些事件作为“可观察事件”,这意味着您可以在模型观察者类中监听它们,就像监听saving
、deleting
等事件一样。
在实现此事件的监听器时,将向回调提供触发事件的模型记录和相关的验证器实例。
\Illuminate\Support\Facades\Event::listen( 'eloquent.validating*', function (Model $model, Validator $validator) { // Do something when any model is "validating". // $model will be an instance of Model and ValidatesAttributes. } );
与其他可观察模型事件类似,此包提供了静态的 validating($callback)
和 validated($callback)
方法,您可以使用这些方法为这些事件注册监听器。
ValidatingModel::validating(function (ValidatingModel $model, Validator $validator) { // ... }) ValidatingModel::validated(function (ValidatingModel $model, Validator $validator) { // ... })
验证器实例
您可以使用 validator()
方法访问最后用于执行 validate()
过程的验证器实例。
$model = new ValidatingModel($request->json()); $validator = $model->validate(); // Or... $model->validate(); $validator = $model->validator();
注意:每次调用
validate()
方法时,都会在模型实例上实例化和存储一个新的验证器实例。
$model = new ValidatingModel(...); $validator1 = $model->validate(); $validator2 = $model->validate(); // $validator1 is *not* a reference to the same object as $validator2.
自定义验证器
您可以在模型上使用 beforeMakingValidator()
和 afterMakingValidator($validator)
方法来自定义验证器实例。
注意:在
afterMakingValidator()
方法中指定验证过程后的钩子是一个不错的选择。
您可以通过 customValidationMessages()
和 customValidationAttributeNames()
方法分别传递自定义验证消息和自定义属性名称到验证器中。
class ValidatingModel extends Model { use ValidatesAttributes; public function customValidationMessages(): array { return ['email.required' => 'We need to know your email address.'] } public function customValidationAttributeNames(): array { return ['ip_v4' => 'ip address']; } }
验证异常
当调用 validate()
方法且验证失败时,默认会抛出 ModelValidationException
。此异常扩展了Laravel的 ValidationException
,但存储了失败验证的模型记录,以便于调试或错误消息处理。
您可以通过覆盖 validationExceptionClass()
或 throwValidationException($validator)
方法来提供自己的验证异常。
测试
composer test
变更日志
有关最近变更的更多信息,请参阅变更日志。
贡献
有关详细信息,请参阅贡献指南。
安全漏洞
有关报告安全漏洞的详细信息,请查看我们的安全策略。
鸣谢
许可
MIT许可(MIT)。有关更多信息,请参阅许可文件。