CakePHPでフォームのバリデーションを行う場合、例えばチェックボックスで「選択肢A」が選択されている場合のみ、「アンケート」のテキストエリアを必須入力にしたい!といった事があるかと思います。
バリデーションルールを切り替えるというのも1つの手段かと思いますが、もっと簡単に、バリデーションの定義だけでそれを実現できる関数を、ビヘイビアとして書いてみたので公開します。
ソースコード
下記のassoc_field_validation.php
をダウンロードして、APP/models/behaviors/
以下に設置してください。
<?php | |
class AssocFieldValidationBehavior extends ModelBehavior | |
{ | |
public function checkAssocField($Model, $check, $assocField, $assocValueList = null, $delegateRule = null) | |
{ | |
$key = 0; | |
if (is_array($check)) { | |
list($key, $check) = each($check); | |
} | |
if (strpos($assocField, '.') !== false) { | |
list($model, $assocField) = $assocField; | |
} else { | |
$model = $Model->alias; | |
} | |
$assocValue = Set::extract($Model->data, "{$model}.{$assocField}"); | |
if ($this->_isValidator($assocValueList) || !$assocValueList) { | |
$assocValueList = null; | |
} | |
if (empty($assocValue) || ($assocValueList && !in_array($assocValue, (array)$assocValueList))) { | |
return true; | |
} | |
if ($this->_isValidator($delegateRule) || !$delegateRule) { | |
$delegateRule = 'notEmpty'; | |
} | |
if (is_array($delegateRule)) { | |
$rule = array_shift($delegateRule); | |
$ruleParams = array_merge(array($check), $delegateRule); | |
} else { | |
$rule = $delegateRule; | |
$ruleParams = array($check); | |
} | |
$Validation = Validation::getInstance(); | |
$methods = array_map('strtolower', get_class_methods($Model)); | |
$behaviorMethods = array_diff(array_keys($Model->Behaviors->methods()), array(__FUNCTION__)); | |
if (in_array(strtolower($rule), $methods)) { | |
$ruleParams[0] = array($key => $check); | |
$valid = $Model->dispatchMethod($rule, $ruleParams); | |
} else if (in_array($rule, $behaviorMethods) || in_array(strtolower($rule), $behaviorMethods)) { | |
$ruleParams[0] = array($key => $check); | |
$valid = $this->Behaviors->dispatchMethod($this, $rule, $ruleParams); | |
} else if (method_exists($Validation, $rule)) { | |
$valid = $Validation->dispatchMethod($rule, $ruleParams); | |
} else if (!is_array($delegateRule)) { | |
$valid = preg_match($rule, $check); | |
} else { | |
$valid = false; | |
} | |
return $valid; | |
} | |
protected function _isValidator($array) | |
{ | |
return (is_array($array) && isset($array['allowEmpty'], $array['required'])); | |
} | |
} |
使い方
ビヘイビアに含まれるcheckAssocField
関数を、バリデーションのルール(ユーザー定義関数)として指定します。
下記のコードは、先ほどの「選択肢A」が選ばれていた場合に「アンケート」の入力を必須入力にする、というサンプルです。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | class Contact extends AppModel { public $validate = array ( 'option' => array ( 'inList' => array ( 'rule' => array ( 'inList' , array ( 'A' , 'B' , 'C' )), 'required' => true, 'allowEmpty' => false, ) ), 'questionnaire' => array ( 'checkAssocField' => array ( 'rule' => array ( 'checkAssocField' , 'option' , array ( 'A' ), 'notEmpty' ), 'message' => '必ず入力してください。' , 'required' => true, ), ), ); } |
checkAssocField関数は3つの引数を取ります。
- assocField (string)
- 値をチェックする入力フォーム名です。
- assocValueList (array)
-
assocField
で指定してた入力フォームの値がここで指定された値ならば、delegateRule
を用いてバリデーションを行います。 - delegateRule (mixed)
-
assocField
で指定したフォームの値がassocValueList
の値に含まれる場合に、実行するバリデーションのルールを指定します。
ここでは他のバリデーションのルールを自由に指定する事が出来ます。
例)array('maxLength', 30)
、array('between', 20, 30)
注意
checkAssocField
関数をバリデーションに利用した場合、そのフィールドのallowEmpty
とrequired
の指定に気をつけて下さい。
例えば上述の例の場合、allowEmpty
をtrue
と指定してしまうと、この関数は意味を成しません。(値が空でもOKということになるため、「選択肢」がAだとしても「アンケート」の値が空で通ってしまう)
また、allowEmpty
をfalse
と指定すると、「選択肢」がAであれBであれ、「アンケート」が空の場合はエラーになってしまうため、これもまた意味がありません。
そこで、required
のみをtrue
とすることで値が空の状態でもバリデーションルールは実行する、という状態にする事が出来ます。
必須のチェックがしたければ、delegateRule
にnotEmpty
を指定する事で行う事ができるので、allowEmpty
ではなくそちらを利用するようにしてください。
この指定にだけは気をつけて下さい。