steven-fox/laravel-model-validation

模型验证的救星。

v0.1.0 2024-05-28 17:00 UTC

This package is auto-updated.

Last update: 2024-09-08 06:17:56 UTC


README

Latest Version on Packagist GitHub Tests Action Status GitHub Code Style Action Status Total Downloads

此包使您能够轻松地为Eloquent模型添加验证助手。虽然已经存在一些类似的包,但此包旨在通过可定制的Laravel风格实现无懈可击的功能。

此包的目的在于

  1. 通过提供一个单点位置来定义模型的基线验证配置,以减少代码重复。
  2. 轻松检索该配置以补充特定的验证场景(如FormRequests、管理表单/操作、控制台输入等)。
  3. 通过确保模型遵守一组特定的规则(独立于UI)来为应用程序提供数据完整性。

主要功能

  • 为您的模型添加了多个验证方法,如validate()passesValidation()failsValidation()
  • 可以将验证配置为在保存模型时自动发生(通过接口进行选择,在需要时可以全局关闭)。
  • 验证规则可以配置为单个定义,也可以根据创建和更新拆分为独立的规则。
  • 可以在运行时覆盖或与自定义规则混合验证规则。
  • 可以在验证之前自定义和转换用于验证的数据。
  • 模型可以定义自定义验证消息和属性名称。
  • 验证配置(数据、规则、消息、名称)可以通过公共方法访问,因此很容易将它们集成到验证过程中,例如请求、控制器、Nova、Filament等。
  • 提供了用于validatingvalidated的模型事件挂钩,易于使用,并且可以用于现有的模型观察者。
  • 可以为特定模型事件定义自定义验证监听器。
  • 提供了用于与需要忽略当前模型记录以进行更新的Unique规则一起工作的助手。
  • 抛出一个包含已验证模型的属性的自定义ValidationException,以帮助调试、记录和错误消息。

安装

您可以通过composer安装此包

composer require steven-fox/laravel-model-validation

用法

ValidatesAttributes特性

通过以下方式将验证功能添加到模型中:

  1. StevenFox\LaravelModelValidation\ValidatesAttributes特性添加到模型中。
  2. 通过以下方法之一在模型上定义规则: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.
}

验证监听器

默认情况下,此包将注册一个事件监听器,用于在保存模型之前执行验证,该监听器针对creatingupdating模型事件。您可以通过在模型上覆盖静态validatingListeners()方法来自定义此行为。以下是默认实现,您可以调整以满足您的需求。

protected static function validatingListeners(): array
{
    return [
        'creating' => ValidateModel::class,
        'updating' => ValidateModel::class,
    ];
}

注意:我们特别使用了creatingupdating事件,而不是更通用的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实例。

如果您需要自定义用于验证的数据属性,您可以通过以下两种方式之一进行操作

  1. 重载rawAttributesForValidation()方法并返回所需的内容。
  2. 重载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一样,您可能会在ServiceProviderboot方法中禁用验证。

// 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...
});

验证模型事件

此包添加了validatingvalidated模型事件。它还注册了这些事件作为“可观察事件”,这意味着您可以在模型观察者类中监听它们,就像监听savingdeleting等事件一样。

在实现此事件的监听器时,将向回调提供触发事件的模型记录和相关的验证器实例。

\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)。有关更多信息,请参阅许可文件