空のフィールドに独自のバリデーションをかける方法
CakePHP3 を使っていて、少し特殊なバリデーションを書く機会に遭遇した。
例)
add.ctp
$this->Form->create($entity); $this->Form->input('hoge'); $this->Form->input('fuga'); $this->Form->submit('submit'); $this->Form->end();
ExamplesController.php
public function add() { $entity = $this->Examples->newEntity(); if ($this->request->is('post') { $tmp = $this->Examples->patchEntity($entity, $this->request->data); if ($this->Examples->save($tmp) { // something } $this->set(compact('entity')); } }
という状況で、
fuga に値が入っている場合は、hoge は何か入力されている必要がある。
hoge に値が入っていても、fugaに値が入っている必要はない。
という仕様を考える。
一番簡単なのは、
ExamplesTable.php
public function validationDefault(Validator $validator) { $validator ->allowEmpty('hoge'); $validator ->allowEmpty('fuga') ->add('fuga', 'custom', [ 'rule' => function ($value, $context) { if (!empty($context['data']['fuga'] && empty($context['data']['hoge']) { return false; } return true; }, ['message' => 'fuga を入力する際は、hogeも入力して下さい'] ]); }
を使って書いていく方法だろう。
ただ、この方法には問題がある。
それは、
「fuga を入力する際は、hogeも入力して下さい」というメッセージが
fuga に対して出現することだ。
本当に指摘したいのは、"hoge" が空欄であることなのに。。。
そして、そのための方法として考えられるのが、
$entity->errors();
を利用してエラーをセットすること。
しかし、残念ながら、 ‘fuga'につけられたエラーを unset する方法を見つけることができなかった。
とうことで、あえなく断念。
たぶん何か方法があるので、ご存じの方は教えてください。
次に考えつく方法は、
public function validationDefault(Validator $validator) { $validator ->add('hoge', 'custom', [ 'rule' => function ($value, $context) { if (!empty($context['data']['fuga'] && empty($context['data']['hoge']) { return false; } return true; }, ['message' => 'fuga を入力する際は、hogeも入力して下さい'] ]); $validator ->allowEmpty('fuga'); }
だ。
しかし、この方法はうまく機能しなかった。
$this->request->data['hoge']
が空文字の場合、
Validation をすり抜けてしまうからだ。
ということで、最終的に見つけたのがこちら。
public function validationDefault(Validator $validator) { $validator ->allowEmpty('hoge', function ($context) { $hoge = $context['data']['hoge']; $fuga = $context['data']['fuga']; if (!empty($fuga) && empty($hoge)) { return false; } return true; }, ['fuga を入力する際は、hogeも入力して下さい''] ); $validator ->allowEmpty('fuga'); }
という書き方だ。
https://api.cakephp.org/3.3/class-Cake.Validation.Validator.html#_allowEmpty
に書いてある。
この方法を使えば、上記の例の要な場合も簡単に対応できる!
いやー、便利ー!