验证

CakePHP 中的验证包提供了构建验证器的功能,可以轻松验证任意数据数组。你可以在 API 中找到可用验证规则的列表.

创建验证器

class Cake\Validation\Validator

验证器对象定义了应用于一组字段的规则。验证器对象包含字段和验证集之间的映射。反过来,验证集包含应用于它们所附加字段的一组规则。创建验证器很简单

use Cake\Validation\Validator;

$validator = new Validator();

创建完后,你可以开始为要验证的字段定义规则集

$validator
    ->requirePresence('title')
    ->notEmptyString('title', 'Please fill this field')
    ->add('title', [
        'length' => [
            'rule' => ['minLength', 10],
            'message' => 'Titles need to be at least 10 characters long',
        ]
    ])
    ->allowEmptyDateTime('published')
    ->add('published', 'boolean', [
        'rule' => 'boolean',
    ])
    ->requirePresence('body')
    ->add('body', 'length', [
        'rule' => ['minLength', 50],
        'message' => 'Articles must have a substantial body.',
    ]);

如上例所示,验证器使用流畅接口构建,允许你为要验证的每个字段定义规则。

上例中调用了一些方法,让我们来了解一下各种功能。 add() 方法允许你将新规则添加到验证器中。你可以单独添加规则,也可以像上面那样成组添加规则。

要求字段存在

requirePresence() 方法要求字段在任何已验证的数组中存在。如果字段不存在,则验证将失败。 requirePresence() 方法有 4 种模式

  • true 字段的存在始终是必需的。

  • false 字段的存在不是必需的。

  • create 在验证创建操作时,字段的存在是必需的。

  • update 在验证更新操作时,字段的存在是必需的。

默认情况下,使用 true。使用 array_key_exists() 检查键的存在,因此空值将计为存在。可以使用第二个参数设置模式

$validator->requirePresence('author_id', 'create');

如果有多个必需字段,可以将它们定义为列表

// Define multiple fields for create
$validator->requirePresence(['author_id', 'title'], 'create');

// Define multiple fields for mixed modes
$validator->requirePresence([
    'author_id' => [
        'mode' => 'create',
        'message' => 'An author is required.',
    ],
    'published' => [
        'mode' => 'update',
        'message' => 'The published state is required.',
    ],
]);

允许空字段

验证器提供了多种方法来控制哪些字段接受空值,以及哪些空值被接受,不会被转发到所命名字段的其他验证规则。CakePHP 为不同形状的数据提供了空值支持

  1. allowEmptyString() 应在只想接受空字符串时使用。

  2. allowEmptyArray() 应在想接受数组时使用。

  3. allowEmptyDate() 应在想接受空字符串或被封送到日期字段的数组时使用。

  4. allowEmptyTime() 应在想接受空字符串或被封送到时间字段的数组时使用。

  5. allowEmptyDateTime() 应在想接受空字符串或被封送到日期时间或时间戳字段的数组时使用。

  6. allowEmptyFile() 应在想接受包含空上传文件的数组时使用。

你也可以使用以下特定验证器: notEmptyString()notEmptyArray()notEmptyFile()notEmptyDate()notEmptyTime()notEmptyDateTime()

allowEmpty* 方法支持一个 when 参数,允许你控制字段何时可以或不能为空

  • false 字段不允许为空。

  • create 在验证创建操作时,字段可以为空。

  • update 在验证更新操作时,字段可以为空。

  • 返回 truefalse 的回调,以指示字段是否允许为空。有关如何使用此参数的示例,请参见条件验证部分。

这些方法在实际中的例子如下

$validator->allowEmptyDateTime('published')
    ->allowEmptyString('title', 'Title cannot be empty', false)
    ->allowEmptyString('body', 'Body cannot be empty', 'update')
    ->allowEmptyFile('header_image', 'update');
    ->allowEmptyDateTime('posted', 'update');

添加验证规则

Validator 类提供了使构建验证器变得简单和富有表现力的方法。例如,为用户名添加验证规则可能如下所示

$validator = new Validator();
$validator
    ->email('username')
    ->ascii('username')
    ->lengthBetween('username', [4, 8]);

请参阅 验证器 API 文档,了解完整的验证器方法集。

使用自定义验证规则

除了使用 Validator 上的方法和来自提供者的方法之外,你还可以使用任何可调用对象(包括匿名函数)作为验证规则

// Use a global function
$validator->add('title', 'custom', [
    'rule' => 'validate_title',
    'message' => 'The title is not valid'
]);

// Use an array callable that is not in a provider
$validator->add('title', 'custom', [
    'rule' => [$this, 'method'],
    'message' => 'The title is not valid'
]);

// Use a closure
$extra = 'Some additional value needed inside the closure';
$validator->add('title', 'custom', [
    'rule' => function ($value, $context) use ($extra) {
        // Custom logic that returns true/false
    },
    'message' => 'The title is not valid'
]);

// Use a rule from a custom provider
$validator->add('title', 'custom', [
    'rule' => 'customRule',
    'provider' => 'custom',
    'message' => 'The title is not unique enough'
]);

闭包或可调用方法在调用时将接收 2 个参数。第一个是将被验证的字段的值。第二个是包含与验证过程相关数据的上下文数组

  • data: 传递给验证方法的原始数据,如果你计划创建比较值的规则,这将很有用。

  • providers: 规则提供者对象的完整列表,如果你需要通过调用多个提供者来创建复杂规则,这将很有用。

  • newRecord: 验证调用是针对新记录还是现有记录。

如果验证通过,闭包应返回布尔值 true。如果失败,则返回布尔值 false,或者对于自定义错误消息,则返回字符串,有关更多详细信息,请参阅条件/动态错误消息部分。

条件/动态错误消息

验证规则方法,无论是自定义可调用对象,还是提供者提供的方法,都可以返回布尔值,指示验证是否成功,也可以返回字符串,表示验证失败,并且返回的字符串应用作错误消息。

通过 message 选项定义的可能存在的错误消息将被验证规则方法返回的错误消息覆盖

$validator->add('length', 'custom', [
    'rule' => function ($value, $context) {
        if (!$value) {
            return false;
        }

        if ($value < 10) {
            return 'Error message when value is less than 10';
        }

        if ($value > 20) {
            return 'Error message when value is greater than 20';
        }

        return true;
    },
    'message' => 'Generic error message used when `false` is returned'
]);

条件验证

在定义验证规则时,可以使用 on 键来定义何时应用验证规则。如果未定义,则始终应用该规则。其他有效值为 createupdate。使用这些值之一将使该规则仅适用于创建或更新操作。

此外,您可以提供一个可调用函数,该函数将确定是否应应用特定规则。

$validator->add('picture', 'file', [
    'rule' => ['mimeType', ['image/jpeg', 'image/png']],
    'on' => function ($context) {
        return !empty($context['data']['show_profile_picture']);
    }
]);

您可以使用 $context['data'] 数组访问其他提交的字段值。上面的示例将根据 show_profile_picture 的值是否为空来使“picture”的规则可选。您还可以使用 uploadedFile 验证规则来创建可选的文件上传输入。

$validator->add('picture', 'file', [
    'rule' => ['uploadedFile', ['optional' => true]],
]);

The allowEmpty*, notEmpty*requirePresence() 方法也将接受回调函数作为其最后一个参数。如果存在,回调函数将确定是否应应用该规则。例如,有时允许字段为空。

$validator->allowEmptyString('tax', 'This field is required', function ($context) {
    return !$context['data']['is_taxable'];
});

同样,当满足某些条件时,可以要求填充字段。

$validator->notEmptyString('email_frequency', 'This field is required', function ($context) {
    return !empty($context['data']['wants_newsletter']);
});

在上面的示例中,如果用户想要接收时事通讯,则 email_frequency 字段不能留空。

此外,还可以要求仅在某些条件下存在字段。

$validator->requirePresence('full_name', function ($context) {
    if (isset($context['data']['action'])) {
        return $context['data']['action'] === 'subscribe';
    }

    return false;
});
$validator->requirePresence('email');

这将要求 full_name 字段仅在用户想要创建订阅的情况下存在,而 email 字段将始终是必需的。

传递给自定义条件回调的 $context 参数包含以下键

  • data 正在验证的数据。

  • newRecord 一个布尔值,指示正在验证新记录还是现有记录。

  • field 当前正在验证的字段。

  • providers 附加到当前验证器的验证提供程序。

将规则标记为最后运行

当字段具有多个规则时,即使先前的规则已失败,也会运行每个验证规则。这使您可以在一次通过中收集尽可能多的验证错误。如果您想在特定规则失败后停止执行,可以将 last 选项设置为 true

$validator = new Validator();
$validator
    ->add('body', [
        'minLength' => [
            'rule' => ['minLength', 10],
            'last' => true,
            'message' => 'Comments must have a substantial body.',
        ],
        'maxLength' => [
            'rule' => ['maxLength', 250],
            'message' => 'Comments cannot be too long.',
        ],
    ]);

如果上面的示例中的 minLength 规则失败,则不会运行 maxLength 规则。

默认情况下使规则为“last”

您可以自动将 last 选项应用于每个规则,可以使用 setStopOnFailure() 方法启用此行为

public function validationDefault(Validator $validator): Validator
{
    $validator
        ->setStopOnFailure()
        ->requirePresence('email', 'create')
        ->notBlank('email')
        ->email('email');

    return $validator;
}

启用后,所有字段将在第一个失败规则处停止验证,而不是检查所有可能的规则。在这种情况下,表单字段下只会出现一条错误消息。

添加验证提供程序

The Validator, ValidationSetValidationRule 类本身不提供任何验证方法。验证规则来自“提供程序”。您可以将任意数量的提供程序绑定到 Validator 对象。Validator 实例会自动设置“默认”提供程序。默认提供程序映射到 Cake\Validation\Validation 类。这使得将该类上的方法用作验证规则变得很简单。当将 Validator 与 ORM 一起使用时,将为表和实体对象配置额外的提供程序。您可以使用 setProvider() 方法添加应用程序需要的任何其他提供程序。

$validator = new Validator();

// Use an object instance.
$validator->setProvider('custom', $myObject);

// Use a class name. Methods must be static.
$validator->setProvider('custom', 'App\Model\Validation');

验证提供程序可以是对象或类名。如果使用类名,则方法必须是静态的。要使用除“默认”以外的提供程序,请确保在您的规则中设置 provider 键。

// Use a rule from the table provider
$validator->add('title', 'custom', [
    'rule' => 'customTableMethod',
    'provider' => 'table'
]);

如果要将 provider 添加到将来创建的所有 Validator 对象,可以使用 addDefaultProvider() 方法,如下所示。

use Cake\Validation\Validator;

// Use an object instance.
Validator::addDefaultProvider('custom', $myObject);

// Use a class name. Methods must be static.
Validator::addDefaultProvider('custom', 'App\Model\Validation');

注意

必须在创建 Validator 对象之前添加 DefaultProviders,因此 config/bootstrap.php 是设置默认提供程序的最佳位置。

您可以使用 Localized 插件 来获取基于国家/地区的提供程序。使用此插件,您将能够根据国家/地区验证模型字段,例如

namespace App\Model\Table;

use Cake\ORM\Table;
use Cake\Validation\Validator;

class PostsTable extends Table
{
    public function validationDefault(Validator $validator): Validator
    {
        // add the provider to the validator
        $validator->setProvider('fr', 'Cake\Localized\Validation\FrValidation');
        // use the provider in a field validation rule
        $validator->add('phoneField', 'myCustomRuleNameForPhone', [
            'rule' => 'phone',
            'provider' => 'fr'
        ]);

        return $validator;
    }
}

Localized 插件使用国家/地区的两位字母 ISO 代码进行验证,例如 en、fr、de。

有一些方法对所有类都是通用的,这些方法通过 ValidationInterface 接口 定义。

phone() to check a phone number
postal() to check a postal code
personId() to check a country specific person ID

嵌套验证器

当使用嵌套数据验证 无模型表单,或者当使用包含数组数据类型的模型时,有必要验证您拥有的嵌套数据。CakePHP 使向特定属性添加验证器变得简单。例如,假设您正在使用非关系型数据库并需要存储文章及其评论。

$data = [
    'title' => 'Best article',
    'comments' => [
        ['comment' => ''],
    ],
];

要验证评论,您将使用嵌套验证器。

$validator = new Validator();
$validator->add('title', 'not-blank', ['rule' => 'notBlank']);

$commentValidator = new Validator();
$commentValidator->add('comment', 'not-blank', ['rule' => 'notBlank']);

// Connect the nested validators.
$validator->addNestedMany('comments', $commentValidator);

// Get all errors including those from nested validators.
$validator->validate($data);

您可以使用 addNested() 创建 1:1“关系”,使用 addNestedMany() 创建 1:N“关系”。在这两种方法中,嵌套验证器的错误都会影响父验证器的错误并影响最终结果。与其他验证器功能一样,嵌套验证器支持错误消息和条件应用。

$validator->addNestedMany(
    'comments',
    $commentValidator,
    'Invalid comment',
    'create'
);

嵌套验证器的错误消息可以在 _nested 键中找到。

创建可重用验证器

虽然在使用的地方内联定义验证器可以生成良好的示例代码,但它不会导致可维护的应用程序。相反,您应该为可重用验证逻辑创建 Validator 子类。

// In src/Model/Validation/ContactValidator.php
namespace App\Model\Validation;

use Cake\Validation\Validator;

class ContactValidator extends Validator
{
    public function __construct()
    {
        parent::__construct();
        // Add validation rules here.
    }
}

验证数据

既然您已经创建了一个验证器并向其中添加了您想要的规则,您就可以开始使用它来验证数据。验证器能够验证数组数据。例如,如果您想在创建和发送电子邮件之前验证联系表格,您可以执行以下操作。

use Cake\Validation\Validator;

$validator = new Validator();
$validator
    ->requirePresence('email')
    ->add('email', 'validFormat', [
        'rule' => 'email',
        'message' => 'E-mail must be valid',
    ])
    ->requirePresence('name')
    ->notEmptyString('name', 'We need your name.')
    ->requirePresence('comment')
    ->notEmptyString('comment', 'You need to give a comment.');

$errors = $validator->validate($this->request->getData());
if (empty($errors)) {
    // Send an email.
}

The getErrors() method will return a non-empty array when there are validation failures. The returned array of errors will be structured like

$errors = [
    'email' => ['E-mail must be valid'],
];

如果您在单个字段上有多个错误,则每个字段将返回一个错误消息数组。默认情况下,getErrors() 方法应用“创建”模式的规则。如果您想应用“更新”规则,您可以执行以下操作。

$errors = $validator->validate($this->request->getData(), false);
if (!$errors) {
    // Send an email.
}

注意

如果您需要验证实体,您应该使用诸如 newEntity(), newEntities(), patchEntity(), patchEntities() 之类的方法,因为它们是为此而设计的。

验证实体数据

验证用于检查来自用于填充实体的表单或其他用户界面的请求数据。

使用 Table 类的 newEntity(), newEntities(), patchEntity()patchEntities() 方法时,会自动验证请求数据。

// In the ArticlesController class
$article = $this->Articles->newEntity($this->request->getData());
if ($article->getErrors()) {
    // Do work to show error messages.
}

类似地,当您需要一次验证多个实体时,可以使用 newEntities() 方法。

// In the ArticlesController class
$entities = $this->Articles->newEntities($this->request->getData());
foreach ($entities as $entity) {
    if (!$entity->getErrors()) {
        $this->Articles->save($entity);
    }
}

The newEntity(), patchEntity(), newEntities()patchEntities() 方法允许您指定要验证的关联以及要应用的验证集,使用 options 参数。

$valid = $this->Articles->newEntity($article, [
    'associated' => [
        'Comments' => [
            'associated' => ['User'],
            'validate' => 'special',
        ],
    ],
]);

除了验证用户提供的数据外,维护数据的完整性也很重要,无论它来自哪里。为了解决这个问题,CakePHP 提供了第二级验证,称为“应用程序规则”。您可以在 应用应用程序规则 部分中阅读有关它们的更多信息。

核心验证规则

CakePHP 在 Validation 类中提供了一套基本的验证方法。Validation 类包含各种静态方法,这些方法为几种常见的验证情况提供验证器。

The API 文档 for the Validation 类提供了一个可用的验证规则及其基本用法的良好列表。

一些验证方法接受额外的参数来定义边界条件或有效选项。您可以按如下方式提供这些边界条件和选项。

$validator = new Validator();
$validator
    ->add('title', 'minLength', [
        'rule' => ['minLength', 10],
    ])
    ->add('rating', 'validValue', [
        'rule' => ['range', 1, 5],
    ]);

接受额外参数的核心规则应该在 rule 键中有一个数组,该数组包含规则作为第一个元素,以及其他参数作为其余参数。