ホーム > タグ > CakePHP

CakePHP

CakePHP 1.2 の Shell から Task のメソッドを呼ぶ方法

基本のキだとは思うんだけど。
シェルの内部からタスクのメソッドを呼び出す方法について,Cookbook はまじめに書いてないのでメモ。
続きを読む

CakePHP の updateAll() で Boolean 型を update しようとしたらハマったお話し

タイトルどおりなんだけど,CakePHP 1.2.10 の話ね。

以下で true として渡してるところがキモ。

< ?php
$delete_conditions = array(
    'Post.id' => $post_ids,
);
$this->updateAll(array('Post.is_deleted' => true), $delete_conditions);
?>

DB で Boolean のカラムなので PHP の Boolean 型を渡すと失敗する。
DboSource::_prepareUpdateFields() の中で,(Boolean)truestr_replace() で置換しようとしてるところがあって,PHP のステキ自動キャストのせいで (Boolean)true -> (Integer)1 -> (String)'1' みたいに変化してるらしくて,最終的に発行される SQL クエリがおかしくなる。

以下のようにすればオッケー。

< ?php
$delete_conditions = array(
    'Post.id' => $post_ids,
);
$this->updateAll(array('Post.is_deleted' => 'true'), $delete_conditions);
?>

'true' と文字列で渡すのがキモ。

他のメソッドは PHP の Boolean で渡せるのに。まったくもー。

1.3 系だと直ってるのか? 2.0 が proof されるまで待つつもりなんだけどな。

CakePHP 1.2.10 を PostgreSQL で使っているときに Fixture で SERIAL 型に INSERT すると SEQUENCE がインクリメントされない

 なんでバグチケットみたいなタイトルなんだろう。まいいや。

 CakePHP でアプリを作ると,モデルへ新規レコードを保存 (AppModel::save()) するときには id (SERIAL 型とか AUTO_INCREMENT 属性とか)のデータを与えず,DB の自動生成に任せることが多いと思う。
 ところが UnitTest の fixture を書くときには,id も固定値でないと想定外の結果になったりして assertion が書けなくなってしまうので,id も与えて INSERT するようにテストデータを定義したい。

 ここで問題が発生する。

 PostgreSQL の場合,自動で挿入した id を記録しておくために SERIAL 型のレコード毎に SEQUENCE を持って保存しているんだが,SEQUENCE は SERIAL 型の DEFAULT 値に設定されている nextval() が生成した id を使っているため, fixture のようにアプリケーションが id を指定して INSERT してしまうと SEQUENCE を更新する奴が誰もいなくなってしまう,というおはなし。
 Cake on MySQL でどうなってるのかは知らん。

 というわけで,これに対処するために CakeTestFixture を継承した AppTestFixture でも作って app/tests にでも置いておく。

< ?php
class AppTestFixture extends CakeTestFixture
{
    function insert(&$db)
    {
        $result = parent::insert($db);
        if (array_key_exists('id', $this->fields)
            && $this->fields['id']['type'] == 'integer'
            && $this->fields['id']['key'] == 'primary'
        ) {
            $db->fetchAll("SELECT pg_catalog.setval(pg_get_serial_sequence('{$this->table}', 'id')"
                . ", (SELECT MAX(id) FROM {$this->table}), true);", false);
        }
        return $result;
    }
}

 あとはこれを継承して fixture を書けばおけ。

< ?php
require_once TESTS . 'app_test_fixture.php';

class HogeFixture extends AppTestFixture
{
    var $name = 'Hoge';
    var $import = array('table' => 'hoges', 'connection' => 'default');
    var $fields = array(
        0 => array(
            'id' => 1,
            'foo' => 'bar',
        ),
        1 => '以下略',
    );
}

CakePHP のあるアクションから,他のアクションの HTML 出力を取得したい

CakePHP (1.2.9) で 自分自身に HTTP ソケット通信する方法。
ふつーは Object::requestAction() で取得できるじゃんとか思うんだろうけど,$this->layout が適用されなくて悩み右往左往した,というおはなし。

とあるコントローラのアクションにて,まず URI をつくる。ホストとか入ってない (ex: /posts/view/1) ので付け足す。

App::import('Helper', 'Html');
$html = new HtmlHelper();
$uri = 'http://localhost' . $html->url(array(
    'controller' => 'posts',
    'action' => 'view',
    'id' => $id,
));

ソケット通信も簡単。
でもこれリクエスト投げてサーバのアクセスログに残ったとこまでしか確認してないから注意ね。検証してくれ。

uses('http_socket');
$socket = new HttpSocket();
$cookies = array();
foreach ($_COOKIE as $name => $val) {
    $cookies[$name] = array(
        'value' => $val,
    );
}

$res = $socket->request( array(
	'method' => 'GET',
	'uri' => $uri,
	'cookies' => $cookies,
));

このコードを実験していて,たいへん遅くてやばいと思ったので 「Object::requestAction() で layout を適用する方法」 を調査するのに戻った。

あった。

$posted_html = $this->requestAction(array(
    'controller' => 'posts',
    'action' => 'view',
    'id' => $id,
), array(
    'return',
    'bare' => 0,
));

'bare' => 0 をつければいけるという情報を英語の Q and A サイトで発見。何という徒労感…… orz

ホーム > タグ > CakePHP

Twitter: clotho_moirai

ページの上部に戻る