CakePHPのバリデーションについて

CakePHP(2.6)のバリデーションについての~~まとめ~~備忘録。
学習（復習）しながらの内容の為、誤りがある場合がございますのでご注意ください。

#はじめに

このドキュメントではモデルに記載されたバリデーションルールがどのように実行されるかという事について検証しています。
検証方法はフォームからの送信による方法ではなく、コード内でそれぞれのフィールドに値を設定し検証している為、CakePHPを用いて作成されたWEBアプリケーションで行われる純粋なバリデーションとは多少異なる部分が存在します。
（フォームフィールド側の入力制限等も広義のバリデーションととらえた場合）


#基本的なルールの記載方法

まずモデルにバリデーションを記載する場合、以下のように記載します。

```php:基本的なルールの記載方法
public $validate = array(
    'フィールド名1' => array(
        'ルール名1' => array(
            'rule' => '適用バリデーション',
        ),
        'ルール名2' => array(
            'rule' => '適用バリデーション',
        )
    ),
    'フィールド名2' => array(
        'ルール名1' => array(
            'rule' => '適用バリデーション',
        )
    )
);
```

「フィールド名」はルールを適用するフィールドで、１つのフィールドに対して複数のルールを適用する事が可能。
例えば、「空を許可せず」且つ「英数字のみ設定可能」というように指定する事ができます。

##ルールの指定

```php
public $validate = array(
    'フィールド名' => array(
        'ルール名' => array(
            'rule'       => '適用バリデーション',
            'required'   => true(or false or 'create' or 'update'),
            'allowEmpty' => true(or false),
            'on'         => ‘create’(or 'update'),
            'message'    => 'エラーメッセージ',
            'last'       => true(or false),
        )
    )
);
```

ルールには｢rule｣｢required｣｢allowEmpty｣｢on｣｢message｣及び｢last｣を１つまたは複数を組み合わせて設定する事が可能。
なお、｢last｣については対象のフィールドに対して複数のルールを設定した場合のみ有効となります。

##｢rule｣キー

｢rule｣キーには適用するバリデーション内容を指定します。
例えば、メールアドレスである事を検証する場合は｢email｣、一意である事を検証する場合は｢isUnique｣を指定します。

[コア・バリデーションルール](http://book.cakephp.org/2.0/ja/models/data-validation.html#core-validation-rules)にて確認できるCakePHPが予め準備しているルールの他、独自に検証ルールを実装する事も可能。
（独自に実装する方法については後述）

｢rule｣キーを指定する場合、オプションの指定が可能です。
例えば、｢email｣の場合第二パラメータとして｢true｣を指定する事でホスト部の有効性の検査も行う事が可能となります。

```php:メールアドレスの妥当性検査
'rule'    => array('email', true),
```

##｢required｣キー

｢required｣キーは指定が難しいキーです。
指定する値は｢true｣又は｢false｣のブール値か、｢create｣又は｢update｣を指定する事ができます。

｢required｣キーに｢true｣が指定されている場合、検証するデータ配列の中にそのフィールドが存在する必要があります。

例えば、以下の二つは同じ結果とはなりません。

```php:Case1a.｢rule｣キーに｢notEmpty｣が指定されているケース
	public $validate = array(
		'field1' => array(
			array(
				'rule'    => 'notEmpty', 
				'message' => 'field1を空にする事はできません', 
			), 
		),
	);
```

```php:Case1b.｢rule｣キーに｢notEmpty｣、｢required｣キーに｢true｣が指定されているケース
	public $validate = array(
		'field1' => array(
			array(
				'rule'     => 'notEmpty', 
				'required' => true, 
				'message'  => 'field1を空にする事はできません', 
			), 
		),
	);
```

これらのルールに以下のデータ配列をセットした場合、異なる結果となります。

```php:検証するデータ配列
		$this->Example->set(
			array(
				'field2' => 'sample', 
			)
		);
```

このデータ配列の場合Case1aの場合は検証を通りますが、一方でCase1bの場合検証を通る事はなくエラーメッセージが表示されることとなります。

これはCase1aはあくまでもフィールドが存在する場合その値を空にする事ができないというルールを設定している為、検証対象となるデータ配列に対象のフィールドが存在しない場合は検証が実施されません。
一方でCase1bの場合はデータ配列にキーが存在する事を必要としている為、このデータ配列のようにフィールドそのものが存在しない場合はエラーとなります。当然データ配列にフィールドが存在する場合は｢rule｣キーに設定されている検証ルールである｢notEmpty｣が検証されることとなります。

｢create｣又は｢update｣が指定された場合、設定されたルールは｢create｣又は｢update｣の場合のみ行われるようになります。
例えば、｢create｣及び｢update｣の両方で行う場合は｢true｣を指定することで常に設定されたルールの検証が実行されます。

なお、｢required｣キーに｢create｣又は｢update｣を指定する方法は、｢on｣キーに｢create｣又は｢update｣を指定することと同じではありません。
｢required｣キーはあくまでもデータ配列にキーが存在する事を必要とするか否かの条件設定であり、｢on｣キーはルールが適用される条件を設定するものです。
その為、例えば｢required｣キーの指定がない又は｢false｣が設定されている状況で｢on｣キーが指定されているようなケースでは、データ配列上の対象となるキーが存在しない場合は、そのルールによる検証は行われません。

##｢allowEmpty｣キー

｢allowEmpty｣キーは対象のフィールドが空である事を容認するか否かの指定を行います。
なお、｢空｣の判定は以下の条件で行われます。

```php:空の判定条件
 !empty($value) || is_numeric($value)
```

なお｢allowEmpty｣キーは｢required｣キーと異なりデータ配列に対象となるキーが存在しない場合は検証が行われません。
その為以下の条件とデータ配列の場合、エラーとはなりません。

```php:検証する条件
	public $validate = array(
		'field1' => array(
			array(
				'rule'       => 'notEmpty', 
				'allowEmpty' => false, 
				'message'    => 'field1を空にする事はできません', 
			), 
		),
	);
```

```php:検証するデータ配列
		$this->Example->set(
			array(
				'field2' => 'sample', 
			)
		);
```

これは前述のとおり｢allowEmpty｣キーは｢required｣キーと異なりデータ配列上に対象のキーが存在する場合のみ｢allowEmpty｣キーに設定された条件の検証が行われるためです。

##｢on｣キー

｢on｣キーは｢create｣又は｢update｣を指定することで検証が行われる条件を設定することができます。
｢create｣が設定された場合は新しいレコードの生成時のみ検証が行われ、｢update｣が設定された場合はレコードの更新時のみ検証が行われることとなります。

なお、｢on｣キーが指定されていない場合はレコードの生成時及びレコードの更新時に検証が行われることとなります。
但し、｢required｣キーとは異なりデータ配列上に対象のキーが存在しない場合は検証が行われない為、もしも必ず設定された検証ルールをレコードの生成時又は更新時に行いたい場合は｢on｣キーと合わせて｢required｣キーにて｢true｣を設定するか、｢required｣キーに対して｢create｣又は｢update｣を設定することが必要となります。

##｢message｣キー

｢message｣キーは該当のルールがエラーとなった場合に返されるエラーメッセージを設定します。

なお｢message｣キーの設定がない場合、独自に実装した検証ルール以外の場合はデフォルトのメッセージとして｢This field cannot be left blank｣が返されます。
独自に実装した検証ルールの場合、検証結果を｢true｣又は｢false｣で返す場合に｢message｣キーの設定がない場合は｢This field cannot be left blank｣が返され、検証結果にエラーメッセージを返す場合はその返されたエラーメッセージが設定されます。

##｢last｣キー

｢last｣キーは対象のフィールドに対して複数のルールが設定されている場合、エラーが発生した場合に該当のルール以降の検証を行うかどうかを設定することができます。
例えば、３つの検証ルールが設定されている場合に全てのルールに｢last｣キーとして｢true｣が設定されている状況で、１つめのルールでエラーが発生した場合は、２つめ及び３つめのルールは検証が行われません。

例えば、以下の条件とデータ配列の場合で結果を確認します。

```php:検証する条件
	public $validate = array(
		'field1' => array(
			array(
				'rule'       => array('maxLength', 10),
				'message'    => 'field1に設定する値は10文字以下にしてください', 
				'last'       => true,
			), 
			array(
				'rule'       => 'numeric',
				'message'    => 'field1は数値を設定してください', 
				'last'       => true,
			), 
		),
	);
```

```php:検証するデータ配列
		$this->Example->set(
			array(
				'field1' => 'abcdefghijklmnopqrstuvwxyz', 
			)
		);
```

上記の内容で検証した場合結果は以下のようになります。

```php:エラーで返される配列
Array
(
    [field1] => Array
        (
            [0] => field1に設定する値は10文字以下にしてください
        )

)
```

このように最大文字数の検証のみが実行され文字種のチェックは行われません。
もし、何れもチェックを行いたい場合は、少なくとも一つめのルールの｢last｣キーに｢false｣を指定することが必要です。
その場合の検証結果は以下のようになります。

```php:１つめのルールの｢last｣キーに｢false｣を指定した場合の結果
Array
(
    [field1] => Array
        (
            [0] => field1に設定する値は10文字以下にしてください
            [1] => field1は数値を設定してください
        )

)
```

#独自検証ルールの作成

標準で準備されているルール以外にも独自で検証ルールを作成し利用することが可能です。
独自の検証ルールを作成するには｢rule｣キーに直接正規表現で検証ルールを指定する方法か、又は検証するメソッドを作成し｢rule｣キーでその検証メソッドを指定するかの何れかの方法で実現できます。

例えば、以下の二つのフィールドに設定された検証ルールはどちらもエラーとなります。

```php:検証する条件
	public $validate = array(
		'field1' => array(
			array(
				'rule'       => '/^[0-9]+$/',
				'message'    => 'field1は数値を設定してください', 
			), 
		),
		'field2' => array(
			array(
				'rule'       => 'myNumericCheck',
				'message'    => 'field2は数値を設定してください', 
			), 
		),
	);

	public function myNumericCheck($check) {
		$value = array_values($check);
		$value = $value[0];
	    return preg_match('/^[0-9]+$/', $value);
	}

```

```php:検証するデータ配列
		$this->Example->set(
			array(
				'field1' => 'abcdefg', 
				'field2' => 'abcdefg', 
			)
		);
```

```php:検証結果
Array
(
    [field1] => Array
        (
            [0] => field1は数値を設定してください
        )

    [field2] => Array
        (
            [0] => field2は数値を設定してください
        )

)
```

なお独自の検証ルールをメソッドで実装する場合、以下のようにオプションを設定することが可能です。

```php:検証する条件
	public $validate = array(
		'field1' => array(
			array(
				'rule'       => array('myLengthCheck', 10), 
				'message'    => 'field1は指定した文字数で入力してください', 
			), 
		),
	);

	public function myLengthCheck($check, $maxLength = 50) {
		$value = array_values($check);
		$value = $value[0];

		if(strlen($value) >= $maxLength){
			return false;
		}

		return true;
	}
```

```php:検証するデータ配列
		$this->Example->set(
			array(
				'field1' => 'abcdefghijklmnopqrstuvwxyz', 
			)
		);
```

```php:検証結果
Array
(
    [field1] => Array
        (
            [0] => field1は指定した文字数で入力してください
        )

)
```

｢myLengthCheck｣では文字列の長さを検証しています。
引数で最大長が指定されている場合、その長さに収まっているいるか確認されます。
もしも引数が省略された場合は、デフォルト値として設定されている｢50｣文字が評価値として設定され検証が行われます。

例では、デフォルト値で｢50｣が指定されているものの引数で｢10｣を設定している為、結果的にフィールドの検査結果はエラーとなります。

#参考

- [データバリデーション(Cookbook 2.x)](http://book.cakephp.org/2.0/ja/models/data-validation.html)