CakePHPで指定したフォームにデータが存在する場合だけ、あるフォームのバリデーションを行うためのビヘイビアを書いてみた

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関数をバリデーションに利用した場合、そのフィールドのallowEmptyrequiredの指定に気をつけて下さい。

例えば上述の例の場合、allowEmptytrueと指定してしまうと、この関数は意味を成しません。(値が空でもOKということになるため、「選択肢」がAだとしても「アンケート」の値が空で通ってしまう)

また、allowEmptyfalseと指定すると、「選択肢」がAであれBであれ、「アンケート」が空の場合はエラーになってしまうため、これもまた意味がありません。

そこで、requiredのみをtrueとすることで値が空の状態でもバリデーションルールは実行する、という状態にする事が出来ます。

必須のチェックがしたければ、delegateRulenotEmptyを指定する事で行う事ができるので、allowEmptyではなくそちらを利用するようにしてください。

この指定にだけは気をつけて下さい。