ぐらめぬ・ぜぷつぇんのはてダ

2008/11/24以降のメインブログはこちらになります。 : http://www.glamenv-septzen.net/

本はてなダイアリにはコメント・トラックバックを受け付ける記事を公開します。

2008-03-06

[]Xhwlay-0.9.3をリリースしました。

Fix Xhwlay_Bookmark_FileStoreContainer's gc() bug and test case bugs.

no title

Bookmarkデータファイルがいつまで経っても消えないなぁ、おかしいなぁと首をかしげてたのですが、調べてみたらBookmarkデータファイル名を"bcdata"とかにしたタイミングで、gc()でのファイル名リストアップでヒットしなくなったため、消されてなかったようです。

しかもテストケースも、ちょうどgc()のテストのとこだけ"_"付けて無効化してたし。

何の為のSimpleTestだよ、って感じ。

とりあえずバグFixアップデートでした。

[]リクエストMETHODを知りたい時など。

AkActionControllerには $Request という名前のメンバ変数でそのものズバリ、AkRequestクラスのインスタンスが入っています。

で、AkRequestクラス経由でリクエストMETHODとかを知ることが出来ます。

AkRequest#getMethod() でリクエストMETHODを文字列で受け取ることが出来ますが、内部的に小文字で扱っているようです。

AkRequest#isPost(), isGet()などのI/Fがありますので、通常はそちらを使った方が良いと思われます。

[] ActiveRecordのモデル(Model)クラスで、テーブル名を手動で調整したいとき($_tableNameメンバ変数)



えっと、Akelosのmodelのgeneratorは特にDBとか多分、見てないです。ですので、table名などは基本的にクラス名それ自体から生成している・・・筈です。すみません、AkActiveRecordのソース追ってみたのですが、正直追い切れませんでした。

getClassForDatabaseTableMapping()の中で

$class_name = get_class($this);
...
$class_name = $this->_getModelName($class_name);
...
@$this->setTableName(AkInflector::tableize($class_name), false);
...
return $class_name;

とあるので多分そうだと思うのですが・・・。

さておき、とにかく題名に対する結論としては、$_tableNameメンバ変数をオーバーライドします。

generatorで生成するmodelクラスもそうですが、AkActiveRecordクラスを継承しています。そこで、$_tableNameはデフォルトではnullになっています。その場合、AkActiveRecord#getTableName()メソッド中で前掲のgetClassForDatabaseTableMapping()が呼ばれてクラス名からテーブル名が生成されます。

というわけで、派生先のクラスで $_tableNameを初期設定しておくことで、テーブル名を手動で指定することが出来ます。

例:app/models/address_book.php :

<?php
class AddressBook extends ActiveRecord
{
    var $_tableName = 'address_book';
}
?>

といいますか、これで合ってるんでしょーか?ActiveRecordの流儀として、大体こんな感じで良いんですかね・・・?

なんかもう、Akelos側で用意してくれてるAPIとかXrefのオンラインHTMLとかも、あるのは良いのですが、文章として曖昧とか知りたい部分が無かったりして、結局ソースを読んだりしてます。JOINを使うようなSQLはADODB経由で全部SQLを自前で用意してますし・・・。

[]AkActionController#redirectTo()メソッドと、Viewのrendering処理について

あるURLにリダイレクトさせたい場合(Akelosの場合はデフォルトでHTTP 302 でLocationヘッダーを送出)、AkActionController#redirectTo()メソッドを使います。

ところで、redirectTo()を呼んだ後、Viewのrendering処理は発生するのでしょうか?

確かにブラウザ側としては302+Locationで、与えられたURLにジャンプすればおしまいですが、PHP側としては、302を出力したなら以降の処理はskipしたいのが正直なところです。例えばrenderingで、layoutやviewの中に、副作用を及ぼす処理が入っているかもしれませんし。

結論としては、viewやlayoutのレンダリングは行われません。もちろんcontrollerのaction中で、redirectTo()の後ろにPHPスクリプトを続けていれば実行されてしまいますが、レンダリングは実行されません。

例:

class FooController extends AkActionController {
    function bar() {
        ...
        if (...) {
            $this->redirectTo('http://...');
        }
        ... // ここはredirectTo()を呼んだ後、実行されてしまいます。
    }
}

この例であれば、どうせlayoutやviewはレンダリングされないのですから、即returnしてしまった方が安心です。

修正後:

class FooController extends AkActionController {
    function bar() {
        ...
        if (...) {
            $this->redirectTo('http://...');
            return;
        }
        ... // redirectTo()を呼べばreturnされるので、実行されません。
    }
}

裏舞台をのぞいてみましょう。

AkActionController :

<?php
...
function redirectTo($options = array(), $parameters_for_method_reference = null) {
    ...
    $this->Response->redirect($options); // ここで実際に HTTP 302 + Locationヘッダーが送出
    $this->Response->redirected_to = $options;
    $this->performed_redirect = true;
    ...
}

function _hasPerformed() {
    return !empty($this->performed_render) || !empty($this->performed_redirect);
}

function process(...) {
    ...
    $this->performActionWithFilters($this->_action_name);

    if (!$this->_hasPerformed()){
        $this->_enableLayoutOnRender ? $this->renderWithLayout() : $this->renderWithoutLayout();
    }
    $this->Response->outputResults();
}
...

redirectTo()で実際にヘッダーが送出されると、"performed_redirect"フラグがONになります。AkActionControllerの起動I/Fであるprocess()メソッド中では、performActionWithFilters()でアクションメソッドを実行した後、_hasPerformed()メソッドでperformed_redirectフラグをチェックし、OFFの時にrendering処理を呼び出しています。

これにより、controllerのaction中でひとたび(あるいはfilter中で)redirectTo()が呼ばれれば、基本的にはrendering処理は行われない事が保証されます。

では、例えば次のようなコードで無理矢理render()を呼ぶことでレンダリング処理をさせる事はできるか?ですが・・・

$this->redirectTo('http://...');
$this->render('....');

render()メソッドの中はかなり入り組んでて、パラメータを調整し直してはrender****に委譲して、さらにその中を追ってみるとまたrenderWith(out)Layout()を呼んでいたりして非常に混沌としていますが・・・。

単純にrender()を呼び出しただけであれば、render()メソッドの冒頭の以下の分岐ではじかれる筈です。(partialbは別格)

function render($options = null, $status = 200) {
    if(empty($options['partial']) && $this->_hasPerformed()){
        $this->_doubleRenderError(Ak::t("Can only render or redirect once per action"));
        return false;
    }
    ...

という感じで、redirectTo()を呼ぶ後のrendering処理は基本的に、Akelos側で自動的に無効化してくれるものと思って大丈夫そうです。

[]どうしてもActiveRecordのモデルを使わずにADODBを直で取り出したいときのAk::db()メソッド

Ak::import()でモデルをrequireし、あとはモデルの中で getConnection() でADODBの接続インスタンスを取り出すのが普通だと思います。

しかし、それでもモデルを使いたくない。・・・モデルクラスをgenerateするのすら厭だという粋な方向けに、Ak::db()メソッドがあります。これはモデルとか一切使わず、直でADODBの接続インスタンスを取得できます。

ですので、generateするのはcontrollerまで。SQLはそりゃあ適宜クラスファイルにまとめてラップするが、とにかくActiveRecordを使いたくない。あるいは、どうしても使えない環境下においては、Ak::db()が便利です。

$con =& Ak::db();
$rs = $con->Execute(...);
...