2013年12月20日金曜日

RIsolutoのコードを読む その5 | Risoluto@アドベント・ぼっち・カレンダー:15日目

ABC(*1)の15日目。共通テーマは私が開発している「Risouto」についてです。



テンプレートエンジンのためのメソッド


さて、前回まではユーザアプリケーションが継承すべきRisolutoControllerBaseクラスについて、あらかじめ用意されているメソッドについて見ていきました。今回もその続きですが、前回はどちらかというとRisolutoの必須メソッド周りについての話だったのに対し、今回は直接ユーザアプリケーションに影響するであろう、「テンプレートエンジン」に関連するメソッドについて見ていきたいと思います。


RisolutoはSmartyを使ってますよっと


Risolutodでは、テンプレートエンジンとしてSmartyを採用してます。他にもいろいろとあるようですが、一番私が使い慣れている……というのがその理由です。Risolutoの標準配布物はSmartyに依存してしまっていますが、ユーザアプリケーション側は別なテンプレートエンジンを使うことができます。単にインストールして、それをユーザアプリケーションから使えるようにするだけ。そこはどうぞご自由に。

で、そのSmartyを使うときなんですけど、3つのメソッドでラップすることでちょっとだけ使いやすくしています。その3つというのは「Smartyオブジェクトインスタンスの生成&初期化」、「テンプレートへのアサイン」、「テンプレートの出力」のこと。まあ、自分で書いてもたいしたことは無いのですが、ちょっとだけすっきり書くことができるはずです。

ってことで、その3つのメソッドを順に見ていきましょう。

オブジェクトインスタンスを作って初期化しますよっと


/**
     * InitTemplate()
     *
     * テンプレートエンジンのインスタンスを生成する
     *
     * @access    protected
     *
     * @param     string $tmpl_path テンプレートファイルが格納されているディレクトリのパス
     *
     * @return    object    テンプレートエンジンのインスタンス
     */
    protected function InitTemplate($tmpl_path = '')
    {
        // テンプレートパスをアプリケーション格納フォルダ配下に限定
        $tmpl_path = RISOLUTO_APPS . 'RisolutoApps/' . str_replace('../', '', $tmpl_path);

        // テンプレートエンジン関連定義(Smartyを使用)
        $tmpl = new Smarty;

        //--- テンプレートキャッシュの設定
        $tmpl->caching              = Smarty::CACHING_OFF;
        $tmpl->cache_dir            = RISOLUTO_CACHE;
        $tmpl->cache_modified_check = true;
        $tmpl->cache_lifetime       = 0;

        //--- コンパイル済みテンプレートの設定
        $tmpl->compile_check = true;
        $tmpl->compile_dir   = RISOLUTO_CACHE;
        $tmpl->force_compile = true;

        //--- テンプレート用コンフィグファイルの設定
        $tmpl->config_dir = $tmpl_path;

        //--- テンプレートのデバッグ設定
        $tmpl->debugging      = false;
        $tmpl->debugging_ctrl = 'NONE';

        //--- テンプレートファイルのパス
        $tmpl->template_dir = $tmpl_path;

        return $tmpl;
    }

まずは「Smartyオブジェクトインスタンスの生成&初期化」のためのメソッドですね。このメソッドは引数を0か1つだけとります(*2)。

この引数は「テンプレートファイルが格納されているディレクトリへのパス」を指定するものです。が、「RISOLUTO_APPS」(*3)ディレクトリ直下にテンプレートファイルがある場合は省略可能です。つまり、「RISOLUTO_APPS」ディレクトリ内に作成したサブディレクトリ中にテンプレートファイルが存在する場合のみ指定すればOK。その場合も、「RISOLUTO_APPS」ディレクトリからの相対パスを指定する形となります(*4)。

このメソッド内では、Smartyオブジェクトを生成して、いろいろな設定を行った後、そのオブジェクトを返却する……というだけのメソッドです。オブジェクトそのものが返却されるので、呼び出し元(*5)では以降の処理のために戻り値を保持しなければなりません。

ちなみにこのメソッドで設定される内容が気にいらない場合は、このメソッドを直接修正するなり、メソッドをオーバーライドして気に入るようにするなり、戻り値として得られたオブジェクトに対して再設定するなり、お好きな方法で変更することが可能です。そこは、各自お好きなように。

テンプレートに値をアサインしますよっと


/**
     * AssignTemplate()
     *
     * テンプレートに表示内容をアサインする
     *
     * @access    protected
     *
     * @param     object $tmpl_instance テンプレートエンジンのインスタンス
     * @param     array  $values        アサイン内容
     *
     * @return    boolean   常にtrue
     */
    protected function AssignTemplate($tmpl_instance, $values)
    {
        // デフォルトでテンプレートに引き渡す情報
        $tmpl_instance->assign('__RISOLUTO_APPS', RISOLUTO_APPS);

        // 与えられた引数の内容をテンプレートにアサインする
        foreach ($values as $key => $val) {
            $tmpl_instance->assign($key, $val);
        }

        // 戻り値を返却
        return true;
    }

続いてテンプレートへのアサイン用メソッド。このメソッドは引数を2つとります。1つ目はSmartyオブジェクトのインスタンス、2つめはアサインする値の連想配列ですね。

このメソッドでは、単にSmarty::assign()をコールしてるだけなので、やってることが気に入らなければこのメソッドを直接修正するなり、メソッドをオーバーライドして気に入るようにするなり、Smarty::assign()を直接コールするなり、まあそういった方法でうまいことできます。ある意味一番いらない子。

このメソッドがやってくれるお節介と言えば、デフォルトで定数「RISOLUTO_APPS」をアサインしてくれることくらい。こいつだけアサインしたい場合は、第2引数に空配列でもセットしておけばいいんじゃないですかね。

テンプレートを出力しますよっと


/**
     * DispTemplate()
     *
     * テンプレートを表示または表示内容を取得する
     *
     * @access    protected
     *
     * @param     object $tmpl_instance テンプレートエンジンのインスタンス
     * @param     string $tmpl_name     テンプレート名
     * @param     string $mode          モード(view:そのまま表示/fetch:表示内容を取得)
     *
     * @return    mixed     $mode=view:常にtrue / $mode=fetch:表示内容/想定外の場合:false
     */
    protected function DispTemplate($tmpl_instance, $tmpl_name, $mode = 'view')
    {
        // 戻り値を保持する変数
        $retval = false;

        // $modeに応じて呼び出すメソッドを変更する
        switch ($mode) {
            // 変数格納時の時
            case 'fetch':
                $retval = $tmpl_instance->fetch($tmpl_name);
                break;

            // 画面表示の時(デフォルト)
            case 'view': // FALL THRU
            default:
                $tmpl_instance->display($tmpl_name);
                $retval = true;
                break;
        }

        // 戻り値を返却
        return $retval;
    }

最後がテンプレートの出力。このメソッドは引数を2つか3つとります。1つ目はSmartyオブジェクトのインスタンス、2つめはテンプレートファイルのファイル名(*6)、3つめはオプションで処理モードです。最後の処理モードだけ解説すればOKだとおもいます。処理モードってのはテンプレートをどこに出力するかを決めるものです。

未指定時や明示的に「'view'」と設定した場合は、Webブラウザにテンプレートエンジンによって処理されたテンプレートファイルを出力します。つまりは画面表示ですね。そうでなくて「'fetch'」を指定した場合は、このメソッドの戻り値として処理内容が返却されるようになります。

「'fetch'」が便利なのはメール文面をテンプレートとして保持しておいて、送信前に値をアサインしてから送る……的な処理するときですね。あとはJSONやRSSとかでHTTPのContent-Typeヘッダを指定してクライアント側に返したい時などに使ってみるといいんじゃないかなと思ってます。

このメソッドも気に入らなければ直接修正するなり、オーバーライドするなり、中で呼んでるメソッドを直接コールするなりすればいいです。お好きにどうぞ。

実際に使うとこんなかんじですよっと


で、こいつらを実際に使ってみた例は……まあRisolutoの標準配布物に含まれている全部のアプリケーションコードなんですが、その中から一番フルで使ってそうなものってことで、「risoluto/apps/RisolutoApps/Sample/Sample02.php」を引用します。

public function Play()
    {
        // ヘッダ情報のセット
        $header = $this->GetDefaultHeader();

        $header['robots'] = 'noindex,nofollow';

        // テンプレートエンジン関連の処理
        $smarty = $this->InitTemplate('Sample/');
        $this->AssignTemplate($smarty, array('header' => $header, 'param' => $this->GetParam(), 'get' => $_GET, 'post' => $_POST, 'server' => $_SERVER));
        $this->DispTemplate($smarty, str_replace(array(__NAMESPACE__, '\\'), '', __CLASS__) . '.tpl');

        return true;
    }

まず「$this->InitTemplate()」で初期化してます。このアプリケーションは「RISOLUTO_APPS/Sample/」ディレクトリにphpとtplが配置されているので、引数に「'Sample/'」を指定しています。

続いて「$this->AssignTemplate()」で値のアサインですね。第1引数には「$this->InitTemplate()」の戻り値として取得したオブジェクトを、第2引数には連想配列として様々な値をセットしています。

最後に「$this->DispTemplate()」でページ表示ですね。第1引数には「$this->InitTemplate()」の戻り値として取得したオブジェクトを、第2引数はちょっとメンドクサイ書き方にしてますけど、自分のネームスペースとクラス名からテンプレートファイル名を生成してます。ここは「Sample02.tpl」のように書くこともできますが、そのままコピペして使える……というのを意図してこんな書き方にしてます。

で、テンプレート側はどうなってるかというと……

{include file="$__RISOLUTO_APPS/common/header.tpl"}
<h1>Risolutoサンプル #2</h1>
<h2>アプリケーションへの値の受け渡し方法について</h2>
<ul>
    <li>アプリケーションに値を渡す際には「.」を使用し、「http://example.com/?seq=Foo_Bar.param1.param2」のように指定します。</li>

    <li>もちろん「http://example.com/?seq=Foo_Bar.param1.param2&param3=param4」のような指定も可能です。</li>

    <li>GETだけではなくPOSTで値を渡すことももちろん可能です。

    <li>$_SERVERなど、PHPで参照できるものについてはRisolutoを使っていても普通に参照できます。</li>
</ul>


<h3>指定された値の表示(Risoluto方式)</h3>
{if !empty($param)}
    <ol>
        {foreach $param as $key => $val}
            <li>{$key|escape:'htmlall':'UTF-8'} -> {$val|escape:'htmlall':'UTF-8'}</li>
        {/foreach}
    </ol>
{else}
    <p>
        このアプリケーションをパラメタを指定して呼び出すと、ここにそのパラメタの内容が表示されます。
    </p>
{/if}

<h3>指定された値の表示(通常のGETパラメタ)</h3>
{if !empty($get)}
    <ol>
        {foreach $get as $key => $val}
            <li>{$key|escape:'htmlall':'UTF-8'} -> {$val|escape:'htmlall':'UTF-8'}</li>
        {/foreach}
    </ol>
{else}
    <p>
        このアプリケーションをGETパラメタを指定して呼び出すと、ここにそのパラメタの内容が表示されます。
    </p>
{/if}

<h3>POSTで渡された値の表示</h3>
{if !empty($post)}
    <ol>
        {foreach $post as $key => $val}
            <li>{$key|escape:'htmlall':'UTF-8'} -> {$val|escape:'htmlall':'UTF-8'}</li>
        {/foreach}
    </ol>
{else}
    <p>
        下のフォームからこのアプリケーションにPOSTすると、ここにその内容が表示されます。
        <form action="?seq=Sample_Sample2" method="post">
            <input type="text" name="post_sample" value="ここに好きな値を入力してください">
            <input type="submit" value="POSTする">
        </form>
    </p>
{/if}


<h3>その他取得可能な情報の表示($_SERVER)</h3>
{if !empty($server)}
    <ol>
        {foreach $server as $key => $val}
            <li>{$key|escape:'htmlall':'UTF-8'} -> {$val|escape:'htmlall':'UTF-8'}</li>
        {/foreach}
    </ol>
{else}
    <p>
        このアプリケーションが取得可能な情報はありません。
    </p>
{/if}

{include file="$__RISOLUTO_APPS/common/footer.tpl"}

こんな感じのコードになります。

最初と最後の「{include〜}」は、Smartyで外部テンプレートを読み込むための書き方ですね。Risolutoではヘッダとフッタを別テンプレートとして用意しており、それを読み込んでるのがこのコード……って感じです。ちなみにヘッダとフッタは「RISOLUTO_APPS/common/」というディレクトリに配置してあるので、「$this->AssignTemplate()」が勝手にアサインしてくれている定数「RISOLUTO_APPS」の値(*7)を使っています。

それ以外は普通にSmartyを使うのと変わらないので、コードを読んだり実際に動かしてみたりすればわかるかとおもいます。

ね?簡単でしょ?


余計なお節介と言われれば返す言葉がないのですが、この程度の比較的簡易なWebページだったら結構簡単に作れてしまったりします。これ以上のDBつかったりするような普通のアプリケーションを書こうと思ったら……他のすばらしいフレームワークを使った方がいいかもしれないです。まあ、そこは皆さんのお好みで。

というわけで今回はこの辺で。いよいよ来週の24日のエントリで最後ですよ!


*1:アドベント・ぼっち・カレンダーの意
*2:つまりオプション
*3:ドキュメントルートに配置したindex.phpで定義される定数
*4:つまりテンプレートファイルは「RISOLUTO_APPS」ディレクトリより上位のディレクトリには配置できないのです
*5:端的に言えばユーザアプリケーション側
*6:ファイル名「のみ」なのに注意されたし
*7:テンプレート上では「$__RISOLUTO_APPS」という変数で参照する

0 件のコメント:

コメントを投稿