3.2用に提案してみる

http://d.hatena.ne.jp/bobchin/20060609
ここで書いた1つのアクションで複数のアクションの結果取得を実現するための変更。
サンプル: http://bobchin.ddo.jp/download/maple_composite.zip

フィルタチェーンの実行は、ConfigUtilsに格納されたmaple.iniのデータを基にしています。
現状では1つのアクションについての情報しか格納しません。


アクションの実行はフィルタで実装されています。
アクションが順に完結して流れていく場合は問題ありません。
(そもそもこれはアクションチェーンの動作(ビューでaction:を指定する場合の動作)ですし・・・)
でも、actionαというアクション内でaction_a, action_b, action_cを実行したい場合に困ります。
actionα内で別のアクションが動作するので、ConfigUtils内のデータを保持しておく必要がでてきます。

参考: http://d.hatena.ne.jp/kunit/20060218 ページ内のサンプル(http://kunit.jp/archives/composite.zip


結局フィルタってのはアクション単位なわけなので、FilterChainとConfigUtilsをアクション名で管理するようにしました。

一方アクションチェーンの方はちょっと考え方が違います。
説明できないんですが、こちらはアクション名ではなくアクションチェーンに名前空間みたいのを持たせました。
特に設定しない場合は"main"という空間でチェーンが実行されます。

サンプルの3つのファイルを、webapp/config/base.iniで変更します。

[ConfigUtils]
name = ConfigUtils2
path = core/ConfigUtils2.class.php

[Request]
name = Request
path = core/Request.class.php

[Response]
name = Response
path = core/Response.class.php

[ActionChain]
name = ActionChain2
path = core/ActionChain2.class.php

[FilterChain]
name = FilterChain2
path = core/FilterChain2.class.php


で、こんな感じにすると、メインのアクション(上でいうactionα)とは別にアクションが実行できます。
アクションチェーンの名前空間を"sub"に設定しています。
要はControllerの実行部分と同じです。
コアに手をいれないようにしてたのでこうなってますが、Controllerが対応すればメソッドを呼ぶだけで済むでしょう。

class Utils_Render
{
    function executeAction($actionName)
    {
        $container =& DIContainerFactory::getContainer();

        $RenderActionChain =& $container->getComponent("ActionChain");
        $backup = $RenderActionChain->setMode('sub');
        $RenderActionChain->clear();
        $RenderActionChain->add($actionName);

        //
        // 実行すべきActionがある限り繰り返す
        //
        while ($RenderActionChain->hasNext()) {
            $RenderconfigUtils =& $container->getComponent("ConfigUtils");
            $RenderconfigUtils->execute($actionName);
    
            //
            // 設定ファイルを元にFilterChainを組み立てて、実行
            //
            $RenderFilterChain =& $container->getComponent("FilterChain");
            $RenderFilterChain->build($RenderconfigUtils, $actionName);
            $RenderFilterChain->execute($actionName);
            $RenderFilterChain->clear();
    
            $RenderconfigUtils->clear($actionName);
            $RenderActionChain->next();
        }

        $RenderActionChain->setMode($backup);
    }
    
    function executeActions($actionNames)
    {
        foreach ($actionNames as $actionName) {
            $this->executeAction($actionName);
        }
    }

}

そのままだとアクション終了後に表示してしまうので、Viewで機能追加しておきます。
やっつけなので汚いですが・・・
ビューで、「block:キー名:アクション名」と指定できるようにして、
Responseオブジェクト内に「キー名」でアクションの結果を保持するようにしてます。
また、ビュー内で「キー名」でアクションの結果にアクセスできるようにしてます。

class Filter_View extends Filter
{
    function execute()
    {
        $isPrint = true;
        if ($view != "") {
                     :
            if (preg_match("/location:/", $template)) {
                     :
            } else {
                // blockの場合は表示しないでResponseオブジェクトに保持する
                if (preg_match("/block:([^:]*):(.*)$/", $template, $match)) {
                    $assignName = $match[1];
                    $template = $match[2];
                    $isPrint = false;
                }
                     :

                // block指定の結果をビューに割り当てる
                if ($isPrint) {
                    $assigns = $response->getAssigns();
                    if (is_array($assigns) && 0 < count($assigns)) {
                        $renderer->assign($assigns);
                    }
                }

                $result = $renderer->fetch($template);
                if ($result != "") {
                    if ($isPrint) {
                        $response->setResult($result);
                    } else {
                        $response->setAssign($assignName, $result);
                    }
                }
            }
        }
                     :
                     :
        if ($redirect) {
                     :
        } else {
            // blockの場合は表示しない
            if ($isPrint) {
                if ($contentDisposition != "") {
                    header("Content-disposition: ${contentDisposition}");
                }
                if ($contentType != "") {
                    header("Content-type: ${contentType}");
                }
    
                print $result;
            } 
        }
    }
}