CakePHP3 中令人困惑的验证与应用程序规则

Confusing Validation vs. Application Rules in CakePHP3

关于验证的多个问题可能属于一起,因为它们都在处理 CakePHP 3 中的新验证概念。

我已阅读有关 CakePHP3 中的验证的章节(1, 2, 3) in the cookbook multiple times but honestly I don't understand how to do it the right way. I also know there is currently a issue/discussion at GitHub,其中可能涉及相同的主题。

触发验证错误,例如与补丁实体。所以我认为在执行保存操作之前总是 check/display 错误更好:

// src/Controller/UsersController.php
public function add() {
  $user = $this->Users->newEntity();
  if ($this->request->is('post')) {
    $user = $this->Users->patchEntity($user, $this->request->data, ['validate' => 'default'] );
    if ( $user->errors() ) {
      $this->Flash->error('There was a Entity validation error.');
    } else {
      // Optional: Manipulate Entity here, e.g. add some automatic values
      // Be aware: Entity content will not be validated again by default
      if ( $this->Users->save($user) ) {
        $this->Flash->succeed('Saved successfully.');
        return $this->redirect(['controller' => 'Users', 'action' => 'index']);
      } else {
        $this->Flash->error('Not saved - ApplicationRule validation error.');
      }
    }
  }
  $this->set('user', $user);
}

为什么食谱教程在保存数据之前不使用 $user->errors()?据我了解,如果已经存在验证错误,则不需要调用 save?!另一种方法是结合错误检查和保存操作:

if ( !$user->errors() && $this->Users->save($user) ) {
  $this->Flash->succeed('Saved successfully.');
  return $this->redirect(['controller' => 'Users', 'action' => 'index']);
} else {
  $this->Flash->error('There was a validation OR ApplicationRule error.');
}

你在用这个吗?我应该使用它吗?或者,如果不是,为什么不呢?

为什么 CakePHP 会显示验证错误,即使我没有在控制器中使用 $user->errors(),就像在所有食谱示例中一样?我以为 save 不会检查实体验证?!

示例:isUnique

根据 cookbook "Ensuring email uniqueness" 是应用程序规则的用例。

// src/Model/Table/UsersTable.php
namespace App\Model\Table;
use Cake\ORM\Table;
use Cake\ORM\RulesChecker;
use Cake\ORM\Rule\IsUnique;
// Application Rules
public function buildRules(RulesChecker $rules) {
  $rules->add($rules->isUnique(['email'], 'This email is already in use'));
  return $rules;
}

错误只会在控制器中调用 save 时触发。但也可以在验证中检查唯一性。为什么不这样做更好?

// src/Model/Table/UserTable.php
namespace App\Model\Table;
use Cake\ORM\Table;
use Cake\Validation\Validator;
public function validationDefault(Validator $validator) {
  $validator
    ->add('email', [
      'unique' => [
        'rule' => 'validateUnique',
        'provider' => 'table',
        'message' => 'This email is already in use'
        ],
      ])
  return $validator;
}

如果我可以在验证中添加 ApplicationRule,为什么 would/should 我完全使用 ApplicationRules?

我如何在 ApplicationRule 中定义规则应仅应用于特定操作(并非所有 create/update 调用)?

patchEntity 调用之后操作实体时,我也没有看到或理解两个独立验证状态的好处。

如果我自动向实体添加一些值,我想确保这些值在保存到数据库之前仍然有效(如在 CakePHP2 中)。所以我猜它是 better/nessecary 到 ALWAYS Using Validation as Application Rules?!

你一般是如何处理这个问题的?是否有其他示例可用于 show/demonstrate Validation 与 ApplicationRules 的好处和一些用例?

我认为您混淆的主要原因是您不知道 save() 如果实体已经包含错误则不会保存该实体。例如:

$entity = $users->newEntity(['email' => 'not an email']);
$users->save($entity); // Returns false

它会 return false 的原因是 save() 在继续实际保存过程之前读取 $entity->errors() 结果。因此,在调用 save() 之前不需要手动检查错误,就像手册中的示例所示。

电子邮件唯一性示例有点棘手,因为您需要检查面向用户的表单(验证的目标)和应用程序规则。

重要的是要记住,Validationvalidation*() 方法一样,旨在向人们提供有关他们提供的数据的反馈。您希望在保存过程开始之前以表单形式显示所有错误(包括嵌套属性的错误)。

由于 Validation 很少发生在数据库事务中,因此无法实际保证电子邮件在验证和保存之间仍然是唯一的。这是应用程序规则试图解决的问题之一:应用程序规则在与其余保存过程相同的事务中执行 运行,因此任何检查都将有一致性保证。

应用程序规则解决的另一个问题是它们可以处理已经在实体上设置的数据,因此您可以完全访问对象的当前状态。这在修补实体或创建新实体时是不可能的,因为任何传递的数据都可能不一致。