cakephpで、viewのcacheではまったのでメモ
cakephpで、レスポンス改善のためviewのhelperを利用したcacheを使おうと思ったのですが、cacheが生成されない現象にはまったので、そのときいろいろ調べたことをメモ。
よく見る利用方法は
コントローラにて、var $helpers = array(‘Cache’);を設定
app/config/core.phpにて、Configure::write(‘Cache.check’, true);を設定(コメントアウトをはずす)
キャッシュしたいアクションのコントローラで、cacheActionをarray(‘action_name’=>’保持期間(秒)’)という形で設定
の3点なのですが、これを実施しただけでは、私の環境では動作しませんでした。
気をつけないといけないポイントは、3つ。
cacheの書き出しは、View::render()。読み込みは、View::renderCache()
ハマリポイント!テンプレートの拡張子を変更したらキャッシュされない!
ハマリポイント!cacheActionは、配列でdurationキーが必要!
cacheの書き出し、読み込み
うまくキャッシュが生成されてなかったので、まず、ソースを追いかけて処理フローを調べてみました。(Cake.version = ‘1.2.3.8166′)
流れとしては、まず、書き込みは、
cake/cake/dispatcher.phpでview::render() (cake/cake/libs/view/view.php)を呼び出す
view::render()がview::_render()を呼び出す
view::_render()の4つ目の引数にtrueが渡っていれば、Cachehelper::cache()が呼びだされる
Cachehelper::cache()がCachehelper::->__writeFile()を呼んで、キャッシュ生成
読み込みは、
dispatcher->cached($url)で読み込もうとしているページのキャッシュの有無を判定
キャッシュがあれば、view::renderCache()を呼び出す
view::renderCache()がキャッシュを読み込む
となります。勘違いしそうなのが、Cacheクラスというのがありますが、それとは関係ないようで、core.phpでCache::configあたりを触っても、影響ありませんでした。(helperのcacheはview専用で、Cacheクラスの方はもっと汎用で高機能なcacheという感じでしょうか。)
ハマリポイント!テンプレートの拡張子を変更したらキャッシュされない!
デフォルトのviewテンプレートの拡張子は、「ctp」ですが、私はこれをapp_controller.phpにて「_html.php」に変えていました。
class AppController extends Controller {
// ビューファイルの拡張子
var $ext = "_html.php";
(・・・以下略)
そして、上のキャッシュの書き出し時のフローで呼び出される、view::_render()メソッドの呼び出し部分の処理が以下(cake/cake/view/view.phpの449行目~)
if (substr($layoutFileName, -3) === 'ctp' || substr($layoutFileName, -5) === 'thtml')
$this->output = View::_render($layoutFileName, $data_for_layout, $loadHelpers, true);
} else {
$this->output = $this->_render($layoutFileName, $data_for_layout, $loadHelpers);
}
$layoutFileNameには書き出すビューファイルのファイル名が入っており、その拡張子部分が「ctp」か「thtml」の場合に、_render()の4つ目の引数がtrueになります。つまり、phpという拡張子では、else側に行ってしまいキャッシュされません。(泣
(※ _render()の定義は、function _render($___viewFn, $___dataForView, $loadHelpers = true, $cached = false){} となっており、$cachedがデフォルトfalseなため)
これって拡張子変更に対応出来てないですよね?(汗
というわけで以下のようにview.php修正して、対応しました。(本当はコアはファイルは触りたくない・・・。何か他に良い方法があれば教えてください><)
// papettoTV modified
// if (substr($layoutFileName, -3) === 'ctp' || substr($layoutFileName, -5) === 'thtml') {
if (substr($layoutFileName, (-1)*strlen($this->ext)) === $this->ext) {
'thtml')
$this->output = View::_render($layoutFileName, $data_for_layout, $loadHelpers, true);
} else {
$this->output = $this->_render($layoutFileName, $data_for_layout, $loadHelpers);
}
設定している拡張子の文字数だけ抜き出して、比較しています。
ハマリポイント!cacheActionは、配列でdurationキーが必要!
これでCachehelper::cache()が呼び出されるようになりましたが、それでもキャッシュファイルが生成されませんでした。ソースを追いかけると(cake/cake/libs/view/helpers/cache.php の136行目~)
if ($cacheTime != '' && $cacheTime > 0) {
$this->__parseFile($file, $out);
if ($cache === true) {
$cached = $this->__parseOutput($out);
$this->__writeFile($cached, $cacheTime, $useCallbacks);
}
return $out;
} else {
return $out;
}
$this->__writeFile()でキャッシュが生成されるようなのですが、$cacheTimeが0だったためif文を通っていませんでした。で、ソースを見る限り$cacheTimeに値が入るようにする方法はいくつかあるようですが、一番安全で確実だと判断して、Controller::cacheAction配列にdurationキーを設定する方法を選びました。もともとこう書いていたものを、
class HogeController extends AppController {
var $cacheAction = array(
"action_name" => 60
);
以下のように変更しました。
class HogeController extends AppController {
var $cacheAction = array(
"duration"=>60
);
アクション名が無くなっています。本当は、アクションごとにdurationを設定したいのですが、マニュアルに書いてる通りやってみても、やはりうまくいかず、とりいそぎ、コントローラ全体をキャッシュする設定で対応しました。(以下の追記にてさらに修正しました)
終わりに
アクションごとに設定できず、少し、気持ち悪い点が残ってますので、今後の課題にしたいと思います。うー、無念
また、今後の動作を確認していく中で、問題があれば報告したいと思います。
追記(2010/01/20)
var $cacheAction はルーティングを利用している場合は、アクション名ではなくURLで指定可能でした!
そこで
class HogeController extends AppController {
var $cacheAction = array(
"fuga" => array("duration" => 60)
);
というようにすることで、/hoge/fuga で一度アクセスすると、60秒間有効なcacheファイルが生成されました。これで、各ページを自在にキャッシュコントロールできそうです。