CakePHP3中的验证与应用规则混淆

7

关于验证的多个问题可能属于同一类,因为它们都涉及到CakePHP 3中的新验证概念。

我已经多次阅读了Cookbook中的章节(1, 2, 3),但是老实说我不知道如何正确地做。我也知道目前在GitHub上有一个关于CakePHP3验证的问题/讨论,可能涉及相同的主题。

例如,使用patchEntity会触发验证错误。因此,我认为在执行保存操作之前,最好始终检查/显示错误:

// 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

根据菜谱,“确保电子邮件唯一性”是应用程序规则的一个用例。

// 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,那么为什么/应该我还要使用ApplicationRules呢?
如何定义ApplicationRule仅在特定操作(而非所有create/update调用)中应用规则?
当实体在patchEntity调用之后被操作时,我也看不到或理解两个分离的验证状态的好处。
如果我自动向实体添加一些值,我希望在将它们保存到数据库之前确保这些值仍然是有效的(就像在CakePHP2中)。所以我猜最好/必要的是始终 将验证作为应用程序规则使用 ?!
你通常如何处理这个问题?是否有其他示例可用来展示/演示验证与ApplicationRules的好处和一些用例?
1个回答

5

我认为你的主要困惑在于不知道 save() 如果实体已经包含错误,则不会保存。例如:

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

它返回false的原因是因为save()在进行实际保存过程之前会读取$entity->errors()的结果。因此,在调用save()之前手动检查错误是不必要的,就像手册中的示例一样。
电子邮件唯一性示例有点棘手,因为这是您想要同时检查用户界面表单(验证针对的内容)和应用程序规则的内容。
重要的是要记住,Validation,即validation*()方法,旨在向人们提供有关他们提供的数据的反馈。您希望在保存过程开始之前呈现表单中的所有错误(包括嵌套属性的错误)。
由于Validation很少发生在数据库事务内,因此无法保证在验证和保存电子邮件之间仍然唯一。这是应用程序规则试图解决的问题之一:应用程序规则确实在与其余保存过程相同的事务中运行,因此在那里进行的任何检查都将具有一致性保证。
应用程序规则解决的另一个问题是它们可以处理已在实体上设置的数据,因此您可以完全访问对象的当前状态。当修补实体或创建新实体时,这是不可能的,因为传递的任何数据都可能不一致。

那么,为了避免在保存数据时出现意外情况,始终使用验证作为应用程序规则是最好的方式吗?我认为这将是确保实体在创建和保存实体之间被操作时仍然有效的唯一方法。您能否给出一些具体的例子,说明应用程序规则绝对比验证更好(不需要代码,只需使用有很大优势的用例)?因为我认为通过条件验证,我可以做到与应用程序规则基本相同。 - Oops D'oh
这里有一个关于免费送货应用规则的示例(http://book.cakephp.org/3.0/en/orm/validation.html#validation-vs-application-rules)。我也可以使用条件验证来实现,例如 return $context['data']['price'] < 100 && $context['data']['shipping_mode'] === 'free'; - Oops D'oh
1
我在我的回答中已经举了一个例子:应用规则在事务中运行,因此它们是确保数据保持一致的唯一方法。特别是对于涉及计算的规则。 - José Lorenzo Rodríguez
1
如果您对应用程序数据的来源存在疑虑,我建议使用验证作为应用程序规则。 - José Lorenzo Rodríguez
好的,这很有道理 - 在条件验证中,我无法确定上下文数据是否有效。但另一个问题也是如何使应用程序规则仅在特定操作中运行?例如:当用户进入表单时,应始终验证免费送货(作为应用程序规则),但如果管理员在同一表格中使用另一个表单,则不应验证(糟糕的例子,但您应该明白)。我可以在patchEntity中定义要使用哪个验证['validate' => 'customValidationForThisAction'],但不能定义哪些应用程序规则(除了一般的addCreate / addUpdate)? - Oops D'oh
不,你不能这样做。当规则依赖于使用情况时,它们属于“验证”。应用程序规则涉及您应用程序中的绝对真理。您可以拥有一个规则来检测原始用户并进行检查。您还可以通过调用 $table->rulesChecker() 并在调用 save() 之前直接操作对象来在运行时更改规则。 - José Lorenzo Rodríguez

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接