beforeMarshal 验证失败时不修改请求数据

beforeMarshal does not modify request data when validation fails

错误还是功能?如果我用 beforeMarshal 更改请求数据并且存在验证错误,则请求数据将不会返回修改。

这个问题可能与有关。

Modifying Request Data Before Building Entities If you need to modify request data before it is converted into entities, you can use the Model.beforeMarshal event. This event lets you manipulate the request data just before entities are created. Source: CakePHP 3 Documentation

根据这本书,我希望请求数据总是更改,无论是否存在验证错误。

示例或测试用例:

// /src/Model/Table/UsersTable.php
namespace App\Model\Table;
use Cake\ORM\Table;
// Required for beforeMarshal event:
use Cake\Event\Event;
use ArrayObject;
// Required for Validation:
use Cake\Validation\Validator;

class UsersTable extends Table {

  public function beforeMarshal(Event $event, ArrayObject $data, ArrayObject $options) {
    $data['firstname'] = trim($data['firstname']);
  }

  public function validationDefault(Validator $validator) {
    $validator
      ->add('firstname', [
        'minLength'   => [ 'rule' => ['minLength', 2], 'message' => 'Too short.' ],
      ])
      ;
    return $validator;
  }
}

如果我输入“d”(Space-d),则会显示验证错误,但 space 本身不会从表单中删除。我会 expact 仅显示 "d" 的表单,因为 space 已通过 beforeMarshal 事件从请求数据中删除。那么...错误或功能?

我的解决方案是在控制器中使用 trim()-函数而不是 beforeMarshal 事件:

// /src/Controller/UsersController.php
// ...
public function add() {
  $user = $this->Users->newEntity();
  if ($this->request->is('post')) {
    // Use trim() here instead of beforeMarshal?
    $this->request->data['firstname'] = trim($this->request->data['firstname']);
    $user = $this->Users->patchEntity($user, $this->request->data );
    if ( $this->Users->save($user) ) {
      $this->Flash->succeed('Saved');
      return $this->redirect(['controller' => 'Users', 'action' => 'index']);
    } else {
      $this->Flash->error('Error');
    }
  }
  $this->set('user', $user);
}

这样,即使出现验证错误,space 也会被删除。还是我漏掉了另一个类似于beforeMarshal的真正修改请求数据的函数?

beforeMarshal 的主要目的是在可以自动解决简单错误或需要重组数据以便将其放入正确的列中时协助用户通过验证过程。

beforMarshal事件在验证过程开始时触发,原因之一是beforeMarshal允许更改验证规则和保存选项,例如字段白名单。此事件完成后立即触发验证。

如文档所述,如果字段未通过验证,它将自动从数据数组中删除,并且不会复制到实体中。这是为了防止实体对象中的数据不一致。

此外,beforeMarshal中的数据是请求的副本。这是因为保留原始用户输入很重要,因为它可能在别处使用。

如果您需要 trim 您的列并向您的用户显示 trim 的结果,我建议在控制器中执行此操作:

$this->request->data = array_map(function ($d) {
    return is_string($d) ? trim($d) : $d;
}, $this->request->data);

不工作。这是我的 beforeMarshal :

 public function beforeMarshal(Event $event, ArrayObject $data, ArrayObject $options)
    {   
        $schema =  $this->schema(); 
        foreach($schema->columns() as $idx => $field  ) {                        
            $sc = $schema->getColumn($field);

            if (isset($data[$field]) &&  $data[$field] != null) { 


     if ($sc['type'] == 'date') {
              $date = DateTime::createFromFormat('d/m/Y',$data[$field]);
              if ($date)
                $data[$field] = $date->format('Y-m-d');
            }
            if ($sc['type'] == 'datetime') {
                debug($data[$field]); 
              $date = DateTime::createFromFormat('d/m/Y',$data[$field]);
              if ($date)
                $data[$field] = $date->format('Y-m-d H:i:s');
            }
        } 
      }  
    debug($data);
}

日期commission_approved_date在beforeMarshal中被正确修改:

/src/Model/Table/AccountsTable.php (line 265)
object(ArrayObject) {
    _referer => 'http://localhost/gessin/Accounts/edit/ODc?filter=eyJBY2NvdW50cy51c2VyX2lkIjoiMTA4NSIsIjAiOiJNT05USChBY2NvdW50cy5jb21taXNzaW9uX2RhdGUpID4gMCIsIllFQVIoQWNjb3VudHMuY29tbWlzc2lvbl9kYXRlKSI6IjIwMjAifQ=='
    description => 'Provvigione su attivazione prodotto vod002'
    notes => 'asd'
    totalpaid => '0'
    commission_approved_date => '2020-02-23 18:34:22'
}

但是日期不一样,在patchEntity之后:

/src/Controller/AccountsController.php (line 203)
object(App\Model\Entity\Account) {

    'id' => (int) 87,
    'identifier' => null,
    'company_id' => null,
    'created' => object(Cake\I18n\FrozenTime) {

        'time' => '2020-02-29 14:01:50.000000+00:00',
        'timezone' => 'UTC',
        'fixedNowTime' => false

    },
    'modified' => object(Cake\I18n\FrozenTime) {

        'time' => '2020-02-29 18:30:24.000000+00:00',
        'timezone' => 'UTC',
        'fixedNowTime' => false

    },
    'notes' => 'asd',
    'total' => null,
    'totaltax' => null,
    'invoice_id' => null,
    'description' => 'Provvigione su attivazione prodotto vod002',
    'in_out' => null,
    'is_refund' => null,
    'client_id' => null,
    'contract_id' => (int) 32,
    'totalpaid' => (float) 0,
    'user_id' => (int) 1085,
    'commission_date' => object(Cake\I18n\FrozenTime) {

        'time' => '2020-02-04 00:00:00.000000+00:00',
        'timezone' => 'UTC',
        'fixedNowTime' => false

    },
    'commission_approved_date' => object(Cake\I18n\FrozenTime) {

        'time' => '2028-08-12 00:00:00.000000+00:00',
        'timezone' => 'UTC',
        'fixedNowTime' => false

    },