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(<?php $javascript->object($complexData) ?>);

if (complex_data["visible"]) {
  // ↑たぶん 0 か 1 によって分岐したいのだろうが
  // 文字列は "0" でも真になるのでこの分岐の意味がなくなる
}

対策は JSON 化する前にデータ型を boolean に変換するか、 data == “1″ のように文字列として比較するかのどちらかが簡単だと思います。

もしかしたら MySQL 以外なら問題なく動くかも。

Model::create() のパラメータ

CakePHP 1.2 RC1 の機能を検証中ですが、日本のフォーラムで注目のトピックがポストされました。

連続したsaveメソッドの使い方について

Beta と RC1 の違いがまたひとつわかりました。

もうひとつ把握している変更点があるのですが、勉強会のネタにからむので、発表の後に公開しようと思います。出し惜しみして何の意味があるのか甚だ疑問ですが。

CakePHP 1.2 RC1 への移行方法

日本有志によるフォーラムにて注目のトピックがポストされました。ベータ版から CakePHP 1.2 RC1 への移行方法です。

Migrating from CakePHP 1.2beta to RC1

今月の中旬には CakePHP ポケットリファレンスの発売が決定しているのですが、さっそく記述が古くなってしまいました。オープンソースの宿命ですね。変更点をまとめたページを制作して公開します。

ただ App::import() は良いデザインだと思いました。モダンです。

小規模 Web アプリケーション開発によく見かける機能をビヘイビアで実装してみる

ホスト言語側での自動採番、削除フラグが真のレコードを取得しない、順序がある…といったモデルを作る際に使えそうなビヘイビアのサンプルです。

ホスト言語側での自動採番

<?php
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;
    }
}
?>

削除フラグが真のレコードを取得しない

<?php
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;
    }
}
?>

順序がある

<?php
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 の存在は大きいと感じました。

ISAPI_Rewrite

CakePHP のキューティーな URL も自由自在ですね。エンタープライズユースの情報は CakePHP を広める上で避けては通れないと思います。 MS 製品で問題なく動くというのはプラスになりますよね。

required と allowEmpty の違い

CakePHP 1.2 からは新しい入力値の検証機構が搭載されていますが、 required と allowEmpty という二律背反的な設定項目があります。

required は「その項目の存在を必須とするか?」の設定で、 allowEmpty は「空の値を許容するか?」の設定です。これを組み合わせると「保存に際して必要ではないけど、保存するなら空の値は許さないよ」というフィールドを作ることができます。たとえばパスワードなんかはこの例に当てはまると思います。これを実現するためには次のように書きます。

<?php
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 と設定しても同じようなことができます。

トップページへ戻る / 前のページへ戻る

back to top