CakePHP

CakePHP hiddenフィールドでパラメータを引き継ぐ

複雑なフォームのパラメータを持ちまわりたかったので、cakephperの日記(CakePHP, MongoDB, Lithium)さんのよくある確認画面でのhiddenデータの持ち回り ver2を改造させていただき無制限でネストしてパラメータに押し込む用にしてみた。

class FormhiddenHelper extends AppHelper {
    var $helpers = array('Form'); 

    function hiddenVars() {
        $ret = "";
        $keyStack = array();
        $this->_hiddenVarsNestParse($this->data, $keyStack, $ret);
        return $ret;
    }

    function _hiddenVarsNestParse($data, &$keyStack, &$ret) {
        if (is_array($data)) {
            foreach ($data as $key => $val) {
                array_push($keyStack, $key);
                $this->_hiddenVarsNestParse($val, $keyStack, $ret);
                array_pop($keyStack);
            }
        } else {
            $ret .= $this->Form->hidden(implode('.' ,$keyStack)) . "\n";
        }
    }
}

CakePHP1.3 saveAllの複数レコード保存と複数モデルの保存の挙動についての注意

CakePHPの動作が難解なのでめも。

1.同一モデルの複数レコードの保存
※insertしたいのでidは指定しない
$data = array(
  'Article' => array(
    '0' => array(
        'name' => 'a',
        'price' => 1000),
    '1' => array(
        'name' => 'b',
        'price' => 5000)));

$this->Article->saveAll($data);

2.単一レコードに結びつく複数モデルの保存
Categoryに結びつくArticleを作成
$data = array(
  'Category' => array(
    'id' => 1),
  'Article' => array(
    '0' => array(
      'name' => 'a',
      'price' => 1000),
    '1' => array(
      'name' => 'b',
      'price' => 5000)));
$this->Article->saveAll($data);

ここで一つ問題が発生
Articleモデルのarticle.phpのvalidateに下記のような記述をした。

$validate = array( 
   'category_id' => array(

CakePHP1.3でredirectを含むコントローラーのテスト

コントローラーの応答にリダイレクトが含まれるとtestActionを実行したときにテストケースが終了してしまうという落とし穴がある。対処方法がリンク先にいろいろ書いてあるが、とりあえず簡単な対処方法が見つかった。

testActionで呼ぶURL部分でcake bakeで作成されたテストケースのTest◯◯Controllerクラスを呼ぶように書き換える。
変更前: $result = $this->testAction('/admin/categories/xxxx');
変更後: $result = $this->testAction('/admin/test_categories/xxxx');

#categories_controller.test.php

class TestCategoriesController extends CategoriesController {
var $autoRender = false;

function redirect($url, $status = null, $exit = true) {
$this->redirectUrl = $url;
}
}

class CategoriesControllerTestCase extends CakeTestCase {
<省略>

function testSomeCase() {
$result = $this->testAction('/admin/test_categories/xxxx');
}

リダイレクトを前提とするテストケースでtestActionに'return' => 'var'を設定するとエラーになる。ビューが呼ばれないのにビューの変数を読み込もうとしてしまったのが原因みたい。

CakePHPでブラウザを閉じたときにセッションが終了するようにする方法

CakePHPのコアをいじらないでブラウザ終了時にセッション終了する方法がわかったので記載する。

 CakePHPはSecurity.level lowまたはmediumだとブラウザを閉じても、セッションが終了しない。これは、PHPの"session.cookie_lifetimeがセキュリティレベルがmediumの時は7日間、lowの時は25年間ぐらいに設定されるためだ。

散々悩んだ挙句にCakePHPのコアのソースを読んでみて、cake/libs/session.phpでいろいろやっていることが分かったので解決方法をここで書きたいと思う。

CakePHPのconfig/core.phpでSession.saveをcakeに設定すると下記の通りセッションの設定がされる。

ini_set('session.use_trans_sid', 0);
ini_set('url_rewriter.tags', '');
ini_set('session.serialize_handler', 'php');
ini_set('session.use_cookies', 1);
ini_set('session.name', Configure::read('Session.cookie'));
ini_set('session.cookie_lifetime', $this->cookieLifeTime);
ini_set('session.cookie_path', $this->path);
ini_set('session.auto_start', 0);
ini_set('session.save_path', TMP . 'sessions');

この設定のsession.cookie_lifetimeの部分だけ入れ替えたいので"config/myapp_session.php"というファイルを準備し上の記述を丸々コピーしてsession.cookie_lifetimeの部分のみ書き換える。

config/myapp_session.phpの内容
------------------------------------------------------
ini_set('session.use_trans_sid', 0);
ini_set('url_rewriter.tags', '');
ini_set('session.serialize_handler', 'php');
ini_set('session.use_cookies', 1);
ini_set('session.name', Configure::read('Session.cookie'));
ini_set('session.cookie_lifetime', 0); // ← この部分を0にした
ini_set('session.cookie_path', $this->path);
ini_set('session.auto_start', 0);
ini_set('session.save_path', TMP . 'sessions');
------------------------------------------------------------

次に/config/core.phpを開き下記の通り変更する
変更前: Configure::write('Session.save', 'cake');
変更後: Configure::write('Session.save', 'myapp_session');

これでmyapp_session.phpの内容を読み込んでくれて、ブラウザを閉じるとセッションも終了するようになる。

以下蛇足

myapp_session.phpが読み込まれる仕組み。

CakePHPコアのsession.phpではSession.saveで不明なタイプが設定されている場合に
下記のプログラムが実行され、"Session.saveで設定された名称" + .phpが読み込まれる

cake/libs/session.php __initSession()の最後のほう

if (empty($_SESSION)) {
$config = CONFIGS . Configure::read('Session.save') . '.php';

if (is_file($config)) {
require_once ($config);
}
}

# 2009/10/3 わかりにくかったので、多少文章変更

コンテンツ配信