【CakePHP】バリデーションでのはまりどころ「required」指定

cake-logoバリデーションについて、勘違いしていたことがあるのでメモ書きをしておきます。

バリデーション指定の中に「required」というパラメータがあるのですが、これは「必ず存在するフィールド」を定義するもののようですね。私は思いっきり「入力の必須項目」と勘違いしていました。

というのは、MediaPluginが上手く動かない場合があってトータル3日くらいはまっていたのですが、実は理由がこれだったのです。
例えば、画像ライブラリを作ると想定して、ファイル情報(つまりAttachment Modelそのもの)の他に「タイトル」や「説明」を含むモデルを作るとしましょう。タイトルや説明を編集したい場合、既にファイルはアップロードされていますから、ファイル関連のフィールドは省きたいところです。しかし、required=>trueとしてしまうと、編集でもファイルアップロードが要求され、更新できなくなります。
同じように、パスワードの変更で、「新しいパスワード」「新しいパスワード(確認)」という項目を作り、両方が正しい場合だけパスワード更新をする、というような場合があります。これも、require=>trueとすると大変なことになります。

動的にフィールドを変更する必要のあるモデルに関しては、requiredは指定しない方がはまりは少ないと感じます。丁寧にやるのなら、「not null」の指定されたフィールドのみ、「on」でcreateとupdateに分け、createにだけrequired=>trueにすると安全かもしれません。 (追記:これはNGでした)

自分だけだったら恥ずかしいのですが、でも勘違いされている人、知らなかった人も多いのではないかと思い書いてみました。いやー、CakePHPは奥が深い!

#でも、required指定は、フィールドに指定するものであって、ruleに指定するものではないような気がする…

【CakePHP】validates()とinvalidate()の記述順

cake-logoバリデーションに関するTipsその2です。

これも意外に情報が書かれていないのですが、Model::validates()とModel::invalidate()の組み合わせ利用では順番があるようで、気をつけないと正しいバリデーション処理が行えないようです。

validates()は「モデルに記述したバリデーションルールを実行する」メソッドで、save()等でも自動的に実行されますが直接呼ぶことで明示的に、事前に処理することが出来ます。
また、invalidate()は、独自に値チェックなどを行った結果で、エラーメッセージを出したい場合に用いるメソッドで、フォーム内のインプットの各アイテムに手動でメッセージを出したい場合に使います。

これらを組み合わせる場合、まずvalidates()が先に実行されている必要があるようです。逆にinvalidate()がvalidates()より先に実行された場合、invalidate()で表示させようとしたエラーメッセージが表示されません。


class FooController extends AppController {

var $name = "Foo";
var $uses = array('Foo');

//OKな例
function bar(){
$this->Foo->set($this->data);
$this->Foo->validates();
$this->Foo->invalidate('fields', 'Good Cake');
}

//NGな例
function hoge(){
$this->Foo->set($this->data);
$this->Foo->invalidate('fields', 'Bad Cake');
$this->Foo->validates();
}

}

バリデーションが上手く機能しない場合はこれが理由である場合もありますので、心当たりがありましたらチェックしてみてください。

【CakePHP】組み込みバリデーションを勝手利用する

cake-logo久しぶりにCakePHP本体のTipsなんかを。

現在ちょっとトリッキー(?)なバリデーションの利用を想定していまして、Model内の$validateプロパティでの指定以外で同機能が使えないものかと調べていました。
組み込みバリデーションは、全て「cake/libs/validation.php内にあるようで、こいつをインポートして直接メソッドを呼び出すだけで何処でも使えるようです。

例えばこんな感じです。

App::import('Core', 'Validation');
class FooController extends AppController {
var $name = 'Foo';

//省略

function hoge(){
//バリデーションの利用
if(Validation::alphaNumeric($check)){
echo "OK牧場";
}

}

なお、場所によってはimport()はいらないかもしれません。

使いどころでは、条件付きでバリデーションを行いたいときですね。
実例を挙げますと、今回管理画面で「新パスワードが入力されたときだけ、新パスワードと確認パスワードをチェックして、問題なければAuthでhashしてsaveする(つまり新パスワードが空の時はパスワード変更処理を除外する)」というのをやっていて、全てをバリデーションルール内で行うのが難しいためです。なので、バリデーションルールから外に出して、関数を直接呼んでチェックしよう、というわけです。

今回は極端な例として、コントローラ内で使用していますが、一般的にはモデル内に独自関数を作ってその中でやった方が良いかもですね。