Controller::paginate() でアソシエーションを有効にする
Controller::paginate() でアソシエーションを有効にするためには、静的なアソシエーションを設定するか Model::bindModel() の第2引数に false を指定して設定をパーシストします。
どうやら Controller::paginate() は内部で何回か find しているようです。
CakePHP トピック目次
Controller::paginate() でアソシエーションを有効にするController::paginate() でアソシエーションを有効にするためには、静的なアソシエーションを設定するか Model::bindModel() の第2引数に false を指定して設定をパーシストします。 どうやら Controller::paginate() は内部で何回か find しているようです。 CakePHP 1.2 の正規表現問題に決着以前のエントリーで正規表現が間違っているのではないかと予想しておりましたが、やっぱり \a2 が正しかったようです。 ちなみにソースには \{00a2} とあるのですが、上位バイトは0でないとまずいのでしょうか? こういう書き方ができるとは知りませんでした。 第3回 CakePHP 勉強会で熱弁した内容がバグだったhttps://trac.cakephp.org/ticket/4870 赤っ恥です。 どうやら PHP4 限定だったみたいです。…ということは PHP4 はオブジェクトを代入するとディープコピーになるのに対し、 PHP5 では普通の参照コピーになるということですよね。 弊社も PHP5 への移行時には総点検が必要のようです。 RC2 の JavascriptHelper::object() がデータ型を見るようになりましたJavascriptHelper::object() といえば、配列を JSON 形式に変換してくれる便利なメソッドですが、どうやら RC2 から仕様が変わったようで、要素のデータ型を反映するようになりました。 つまり、以前は数値に見えるものは数値として出力していたところを、 is_string() などを参考にして、文字列型と判断された場合はダブルクォーテーションを付加して出力します。 たとえば以下のようなコードは正しく動作しなくなります。 // $complexData["visible"] にデータベースから取得した boolean 値が入っている時… var complex_data = eval($javascript->object($complexData) ); if (complex_data["visible"]) { // ↑たぶん 0 か 1 によって分岐したいのだろうが // 文字列は "0" でも真になるのでこの分岐の意味がなくなる } 対策は JSON 化する前にデータ型を boolean に変換するか、 data == “1″ のように文字列として比較するかのどちらかが簡単だと思います。 もしかしたら MySQL 以外なら問題なく動くかも。 Model::create() のパラメータCakePHP 1.2 RC1 の機能を検証中ですが、日本のフォーラムで注目のトピックがポストされました。 Beta と RC1 の違いがまたひとつわかりました。 もうひとつ把握している変更点があるのですが、勉強会のネタにからむので、発表の後に公開しようと思います。出し惜しみして何の意味があるのか甚だ疑問ですが。 CakePHP 1.2 RC1 への移行方法日本有志によるフォーラムにて注目のトピックがポストされました。ベータ版から CakePHP 1.2 RC1 への移行方法です。 Migrating from CakePHP 1.2beta to RC1 今月の中旬には CakePHP ポケットリファレンスの発売が決定しているのですが、さっそく記述が古くなってしまいました。オープンソースの宿命ですね。変更点をまとめたページを制作して公開します。 ただ App::import() は良いデザインだと思いました。モダンです。 小規模 Web アプリケーション開発によく見かける機能をビヘイビアで実装してみるホスト言語側での自動採番、削除フラグが真のレコードを取得しない、順序がある…といったモデルを作る際に使えそうなビヘイビアのサンプルです。 ホスト言語側での自動採番class HostSideIncrementableBehavior extends ModelBehavior { var $__defaults = array("increment_field" => "order"); function setup(&$model, $config = array()) { $config = array_merge($this->__defaults, $config); $this->settings[$model->alias] = $config; } function beforeSave(&$model) { $field = $this->settings[$model->alias]["increment_field"]; if (empty($model->data[$model->alias]["id"]) && empty($model->data[$model->alias][$field])) { $order = array($model->alias => array($field => "DESC")); $result = $model->find(null, null, $order, 1); if (empty($result)) { $next = 1; } else { $next = $result[$model->alias][$field] + 1; } $model->data[$model->alias][$field] = $next; } return true; } } 削除フラグが真のレコードを取得しないclass SoftwareDeletableBehavior extends ModelBehavior { var $__defaults = array("visible_field" => "visible", "visible_value" => true); function setup(&$model, $config = array()) { $config = array_merge($this->__defaults, $config); $this->settings[$model->alias] = $config; } function beforeFind(&$model, $query) { $field = $this->settings[$model->alias]["visible_field"]; $visible = $this->settings[$model->alias]["visible_value"]; if (is_array($query["conditions"])) { $query["conditions"][$model->alias. ".". $field] = $visible; } else if (strlen($query["conditions"])) { $query["conditions"] = array($query["conditions"]); $query["conditions"][$model->alias. ".". $field] = $visible; } else { $query["conditions"] = array(); $query["conditions"][$model->alias. ".". $field] = $visible; } return $query; } } 順序があるclass SortableBehavior extends ModelBehavior { var $__defaults = array("order_field" => "order", "order_direction" => "ASC"); function setup(&$model, $config = array()) { $config = array_merge($this->__defaults, $config); $this->settings[$model->alias] = $config; } function beforeFind(&$model, $query) { $field = $this->settings[$model->alias]["order_field"]; $direction = $this->settings[$model->alias]["order_direction"]; if (is_array($query["order"])) { $query["order"][$model->alias] = array($field => $direction); } else if (strlen($query["order"])) { $query["order"] = array($query["order"]); $query["order"][$model->alias] = array($field => $direction); } else { $query["order"] = array(); $query["order"][$model->alias] = array($field => $direction); } return $query; } } やはり CakePHP 1.2 はオブジェクト指向が加速しますね。 セキュリティ対策の実用書に CakePHP の記事セキュア Web プログラミング Tips 集という本に CakePHP の記事がありました。 中身は比較演算子インジェクションですので有名な話ですが。フレームワークの注目度の高さがうかがえますよね。 注目すべきは、当ブログで何度か話題にしております、壊れた UNICODE 問題に対する解答案が載っていた点です。基本的には他エンコードに変換したのち逆変換をかけることでバイトストリームを正規化するという手法が有効のようです。 他にも IIS + PHP 独特の問題に対する安全策など得るものが多くありました。自分は不勉強でした。 IIS6 + SQL Server + ISAPI_Rewrite で CakePHP を動かすCakePHP on IIS6 with FastCGI, SQL Server 2005, and ISAPI_Rewrite 本家 Baker に MS 製品環境下での CakePHP セットアップ方法が載っていました。 詳細な手順はリンク先にて参照いただければと思いますが mod_rewrite と近い記述で IIS による URL 書き換えが実現できる ISAPI_Rewrite の存在は大きいと感じました。 CakePHP のキューティーな URL も自由自在ですね。エンタープライズユースの情報は CakePHP を広める上で避けては通れないと思います。 MS 製品で問題なく動くというのはプラスになりますよね。 required と allowEmpty の違いCakePHP 1.2 からは新しい入力値の検証機構が搭載されていますが、 required と allowEmpty という二律背反的な設定項目があります。 required は「その項目の存在を必須とするか?」の設定で、 allowEmpty は「空の値を許容するか?」の設定です。これを組み合わせると「保存に際して必要ではないけど、保存するなら空の値は許さないよ」というフィールドを作ることができます。たとえばパスワードなんかはこの例に当てはまると思います。これを実現するためには次のように書きます。 class User extends AppModel { var $name = "User"; var $validate = array("user_id" => array(array("rule" => array("minLength", 4), "required" => true, "allowEmpty" => false)), "password" => array(array("rule" => array("minLength", 8)), "required" => false, "allowEmpty" => false)); } この場合、 user_id は required が true なので、いかなる場合も user_id のフィールドが存在している必要がありますが、 password のフィールドはなくても保存することができるようになります。ただし、 password のフィールドを用意した場合は値を入力しなければなりません。 ちなみに password の on に create と設定しても同じようなことができます。 |