[CakePHP3][Validation]0と自然数のみ許可するValidationのかけ方


<編集 2016/12/15 10:21>
<del>昨夜投稿した分ですが、あいてたので。。。</del>
CakePHP3 Advent Calendar 15日目です。
<編集終わり>

# 小ネタです

CakePHP3 で、数値に自然数というValidation をかけたかったけど、ちょっとハマった。
ついでに、Validationが、内部でどんなことをやってるか見たことがなかったので見てみた。

## 自然数のValidation

```php
$validator
    ->integer('hoge')
    ->requirePresence('hoge', 'create')
    ->notEmpty('hoge')
    ->naturalNumber('hoge', __('[FATAL] not a natural number.'));
```

これだけで、自然数のValidation はできる。
つまり、`hoge` が、 `0` 以外だったら `false` になる。

これは簡単だ。
ただ、これを探していた時に見た、
http://api.cakephp.org/3.1/class-Cake.Validation.Validation.html#_naturalNumber
に、

```
boolean $allowZero optional false
Set true to allow zero, defaults to false
```
と書いてあった。

盛大に意訳すると、`自然数`だけでなく、`自然数と0` というValidation をかけることもできると書いてある。

なるほど。
それは面白い。

ということで、やってみた。

## First Try

```php
$validator
    ->integer('hoge')
    ->requirePresence('hoge', 'create')
    ->notEmpty('hoge')
    ->naturalNumber('hoge', true,  __('[FATAL] not a natural number.'));

```

だが、うまく動かない。
それどころか、メッセージも表示されない。

困った。

## Second Try

```php
$validator
    ->integer('hoge')
    ->requirePresence('hoge', 'create')
    ->notEmpty('hoge')
    ->naturalNumber('hoge', [true],  __('[FATAL] not a natural number.'));

```
なんとなく、配列にしてみた。
結果は何も変わらない。


ということで、よくわからないから中身を見てみた。
見たのは、
http://api.cakephp.org/3.1/class-Cake.Validation.Validation.html#_naturalNumber

にあるように、`Cake\Validation\Validation`の中。
(リンクは、`Cake\Validation\Validation` のAPIについて書かれている)

そして、その中に、ルールが書いてあった。

```php
public static function naturalNumber($check, $allowZero = false)
{
    $regex = $allowZero ? '/^(?:0|[1-9][0-9]*)$/' : '/^[1-9][0-9]*$/';

    return static::_check($check, $regex);
}

```

なんか、ちゃんと書かれてるっぽい。
なんで動かないんだろう？

ということで、念のため First Try の形に戻して debug してみた。

```php
$validator
    ->integer('hoge')
    ->requirePresence('hoge', 'create')
    ->notEmpty('hoge')
    ->naturalNumber('hoge', [true],  __('[FATAL] not a natural number.'));

```

```php
public static function naturalNumber($check, $allowZero = false)
{
    debug($check);
    debug($allowZero);
    exit;
    $regex = $allowZero ? '/^(?:0|[1-9][0-9]*)$/' : '/^[1-9][0-9]*$/';

    return static::_check($check, $regex);
}
```
結果

```
1
false
```

ん？ `__('[FATAL] not a natural number.')` すら来てない。

ということで、一度冷静になって考えてみた。
なんでだろう？

そして、ヒントはBake で出てくるコメントに書いてあった。

```php
public function validationDefault(Validator $validator)
```
これ、見るところ違ったんじゃない？
`Validator` と 書いてあるではないか。
さっきまで見ていたのは`Validation`。
そもそもメソッドチェーンができるのは、`$validator` が `Validator` のインスタンスだからのはず。
これは、何かヒントがあるに違いない！！！
ということで、場所を変更して`Cake\Validation\Validator` を見た。
すると、書いてあるではないか！！！

```php
public function naturalNumber($field, $message = null, $when = null)
{
    $extra = array_filter(['on' => $when, 'message' => $message]);

    return $this->add($field, 'naturalNumber', $extra + [
        'rule' => ['naturalNumber', false]
    ]);
}
```

あれ？
これ、ラッパーしてるだけみたい。
しかも、

```php
->naturalNumber('hoge')
```
の形だと、`$allowZero`オプションが強制的に `false` になるみたい。。。

つまり、そもそも今までやってたやり方では実現できなかったらしい。

ということで正解は、

```php
->add('hoge', 'naturalNumber',
 [
     'rule' => ['naturalNumber', true],
     'message' => '自然数で入力してください'
]);
```

でした。

それにしても、`Validator` が中で `return $this->add()`してるとは思ってなかった。
面白かった。

以上、小ネタでした。