gorogoroyasu

福岡の開発会社で働いている。

空のフィールドに独自のバリデーションをかける方法

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
に書いてある。

この方法を使えば、上記の例の要な場合も簡単に対応できる!
いやー、便利ー!