iPhone/iPadのホーム画面コンテスト開催中!賞品は iTunes カード!

CakePHP & PHP 2007/04/26 17:58

CakePHP モデルのvalidates()に注意

Model#validates()ですが、「ちょっとどうなの?」な仕様になってます。

このメソッドはModel#save()内で自動的に呼ばれるのか、コントローラのアクションメソッド内で呼ばれるのが一般的な使い方だと思います。

問題が起こるのは後者の方で、空の配列をvalidates()に渡すと、Userl#$validateで何を定義していてもtrueが返ってきます。

下のソースならUserモデルの$validateに入力チェックをいれておけば「ng」が表示されそうですが、実はUser#$validateに関わらず「ok」が表示されてしまいます。

PHP:
  1. <?php
  2. class FooController extends AppController
  3. {
  4.     var $uses = array('User');
  5.  
  6.     function blank()
  7.     {
  8.         $this->data = array();
  9.         if ($this->User->validates($this->data)) {
  10.             var_dump('ok');
  11.         } else {
  12.             var_dump('ng');
  13.         }
  14.  
  15.         exit;
  16.     }
  17. }
  18. ?>

原因はModel#invalidFields()の以下の箇所にあります。(invalidFields()はvalidates()から呼ばれます)

[cake/libs/model/model_php4.php]

PHP:
  1. <?php
  2.  
  3.         foreach($this->validate as $field_name => $validator) {
  4.             if (isset($data[$field_name]) && !preg_match($validator, $data[$field_name])) {
  5.                 $this->invalidate($field_name);
  6.             }
  7.         }
  8.  
  9. ?>

$dataはvalidates()に渡される連想配列なのですが、$dataに$validateのキーが存在しない場合はpreg_match自体が処理されず、invalidate()にはなりません。

なのでコントローラからvalidates()を呼ぶ時は、$dataにModel#$validateのキーに存在するかを確認する必要があります。

ちなみにModel#save()ではvalidates()の後の処理でfalseが返るようになっています。ただこれも$dataが空の配列の場合の話で、Model#$validateにキーは無い(入力チェックはしない)が、テーブルにカラムがあるキーのみの配列なら通ってしまいます。

例えば下のソースなら「ok」が表示されます。

PHP:
  1. <?php
  2.     function blank()
  3.     {
  4.         // User#$validate に id が無い場合
  5.         $this->data = array('id' => '');
  6.         if ($this->User->save($this->data)) {
  7.             var_dump('ok');
  8.         } else {
  9.             var_dump('ng');
  10.         }
  11.  
  12.         exit;
  13.     }
  14. }
  15. ?>

やはりこちらも入力チェックするキーが$dataに存在するかを確認するしか無いようです。



■Related Posts

7 Responses to “CakePHP モデルのvalidates()に注意”

  1. on 27 4月 2007 at 09:33 1.yando said …

    必須項目のキーをプロパティに設定しておき、validatesの際に存在チェックを事前に行うようにオーバーライドですかねー。

  2. on 27 4月 2007 at 09:42 2.shinbara said …

    @yandoさん
    ですね。

    はじめAppModel#beforeValidate()でそれをやろうと考えたのですが、Model#saveField()の時に困るんですよね。
    users.statusだけ変えたいのにusers.nameのバリデートに引っかかるとか。
    多分saveField()のためにこんな仕様になってるんでしょうね。

  3. on 05 11月 2007 at 16:25 3.sylvan :: 対応が必要なモデルの validates said …

    [...] CakePHP モデルのvalidates()に注意 | Shin x blog には対応する必要がありそう。 [...]

  4. on 04 12月 2008 at 21:52 4.slywalker said …

    array(
    'rule' => array('notempty'),
    'required' => true,
    ),
    );
    }
    ?>
    上記のように、'required' => true で'ng'でました!
    さらに、saveFeildも試しました。
    $this->User->saveField('password', 'hoge')
    問題なく保存できました!

  5. on 04 12月 2008 at 21:54 5.slywalker said …

    すいませんコード部分が消えたので補足です^^;

    var $validate = array(
    'name' => array(
    'rule' => array('notempty'),
    'required' => true,
    ),
    );

  6. on 04 12月 2008 at 22:17 6.slywalker said …

    2007/04/26 17:58
    思いっきり日付を勘違いしていました。
    はてブの新着エントリーにあったので・・・
    _| ̄|○

  7. on 17 2月 2009 at 13:02 7.arimaster said …

    slywalkerさんのやり方を参考にさせて頂いたのですが、
    私の方ではなぜか必ずfalseになってうまくいきませんでした・・・。

    こちらのサイト(http://tech.li-pton.com/php/validation_trap.html#)を参考に
    「createメソッドでデータをセットし、validatesメソッドは引数なしで呼び出す」とうまくいきました。

    if($this->User->create($this->data) && $this->User->validates()){

    }else{

    }

Trackback This Post | Subscribe to the comments through RSS Feed

Leave a Reply