EthnaでテンプレートにDBを使用する方法

ちょっと、IRCで話題になったので作成してみました。
Ethnaのテンプレートに DB内に入っているリソースを使用する方法についてです。


DB内に格納されているデータを、テンプレートとして使用する方法として、Smartyのリソースプラグインを使用するのがお手軽でいいかと思います。
http://www.smarty.net/manual/ja/plugins.resources.php


サンプルとして、sampleというアプリケーションIDで作成してみます。

まずは、環境作成

ethna.shは、Ethna本体をインストールせずに使用するためです。
Smartyは、あらかじめ、パスの通してある箇所に展開済みとします。

$ mkdir sample
$ cd sample
$ svn export http://svn.sourceforge.jp/svnroot/ethna/ethna/tags/ETHNA_2_3_5/ lib/Ethna
$ echo -e '#!/bin/sh\nexport ETHNA_HOME=$(dirname $0)/lib/Ethna\n$ETHNA_HOME/bin/ethna.sh "$@"' > ethna.sh


次に、プロジェクト作成

$ sh ethna.sh add-project -b ../ sample


後で、差分を確認するために、作成直後にコピーしておきます。
本来は必要ありません。

$ cp ../sample sample_base

テーブル構造

今回は、以下のような構造だとします。

create table tpl (name varchar(32),source blob,updatetime timestamp, primary key (name));

insert into tpl (name,source) values ('index','<html><body><h1>index</h1>index</body></html>');

実際には、期間切り替えやその他情報も入れる必要があるでしょう。


また、このDBに接続できるように、etc/sample-ini.phpに設定が追記する必要があります。

いじくる箇所

以下の箇所を変更します。

  1. Smartyプラグインを置くディレクトリを作成
  2. app/Sample_Controller.php に 作成したディレクトリへのパスを追加
  3. リソースプラグイン作成
  4. 該当Viewを変更(今回はIndex.php
Smartyプラグインを置くディレクトリを作成
$ mkdir app/plugin/Smarty
app/Sample_Controller.php に 作成したディレクトリへのパスを追加

$directoryとういプロパティに、Smartyプラグインディレクトリへのパスを追加します。

$ vi app/Sample_Controller.php
<?php //色づけ
    /**
     *  @var    array       アプリケーションディレクトリ
     */
    var $directory = array(
()
        'locale'        => 'locale',
        'log'           => 'log',
        'plugins'       => array('app/plugin/Smarty'), //追加
        'template'      => 'template',
        'template_c'    => 'tmp',
()
    );
リソースプラグイン作成
$ vi app/plugin/Smarty/resource.db.php
<?php
/*
 * Smarty plugin
 * -------------------------------------------------------------
 * File:     resource.db.php
 * Type:     resource
 * Name:     db
 * Purpose:  Fetches templates from a database
 * -------------------------------------------------------------
 */
function smarty_resource_db_source($tpl_name, &$tpl_source, &$smarty)
{
    $ctl =& Ethna_Controller::getInstance();
    $backend =& $ctl->getBackend();
    $db =& $backend->getDB();
    $sql = "SELECT source, updatetime FROM tpl WHERE name = ?";
    $result = $db->db->getRow($sql, array($tpl_name), DB_FETCHMODE_ASSOC);
    if ($result && !DB::isError($result)) {
        $tpl_source = $result['source'];
        return true;
    } else {
        return false;
    }
}

function smarty_resource_db_timestamp($tpl_name, &$tpl_timestamp, &$smarty)
{
    $ctl =& Ethna_Controller::getInstance();
    $backend =& $ctl->getBackend();
    $db =& $backend->getDB();
    $sql = "SELECT source, updatetime FROM tpl WHERE name = ?";
    $result = $db->db->getRow($sql, array($tpl_name), DB_FETCHMODE_ASSOC);
    if ($result && !DB::isError($result)) {
        $tpl_timestamp = $result['updatetime'];
        return true;
    } else {
        return false;
    }
}

function smarty_resource_db_secure($tpl_name, &$smarty)
{
    // assume all templates are secure
    return true;
}

function smarty_resource_db_trusted($tpl_name, &$smarty)
{
    // not used for templates
}
?>

2回SQLをなげてしまっているので、実運用時にはもう一工夫必要です。

該当Viewを変更(今回はIndex.php
$ vi app/view/Index.php

以下のメソッドを追加し、Ethna_Viewのメソッドをオーバーライドします。

<?php //色づけ
    /**
     *  遷移名に対応する画面を出力する
     *
     *  特殊な画面を表示する場合を除いて特にオーバーライドする必要は無い
     *  (preforward()のみオーバーライドすれば良い)
     *
     *  @access public
     */
    function forward()
    {
        $renderer =& $this->_getRenderer();
        $this->_setDefault($renderer);
        $this->forward_path = 'db:'. basename($this->forward_path, '.tpl');
        $renderer->engine->display($this->forward_path);
    }

最後に

全ての変更でdiffをとってみたらこんな感じ。

$ diff -r -U2 -P sample_base sample
diff -r -U2 -P sample_base/app/Sample_Controller.php sample/app/Sample_Controller.php
--- sample_base/app/Sample_Controller.php       2008-06-25 14:00:27.000000000 +0900
+++ sample/app/Sample_Controller.php    2008-06-25 13:59:43.000000000 +0900
@@ -97,5 +97,5 @@
         'locale'        => 'locale',
         'log'           => 'log',
-        'plugins'       => array(),
+        'plugins'       => array('app/plugin/Smarty'),
         'template'      => 'template',
         'template_c'    => 'tmp',
diff -r -U2 -P sample_base/app/plugin/Smarty/resource.db.php sample/app/plugin/Smarty/resource.db.php
--- sample_base/app/plugin/Smarty/resource.db.php       1970-01-01 09:00:00.000000000 +0900
+++ sample/app/plugin/Smarty/resource.db.php    2008-06-25 15:09:36.000000000 +0900
@@ -0,0 +1,51 @@
+<?php
+/*
+ * Smarty plugin
+ * -------------------------------------------------------------
+ * File:     resource.db.php
+ * Type:     resource
+ * Name:     db
+ * Purpose:  Fetches templates from a database
+ * -------------------------------------------------------------
+ */
+function smarty_resource_db_source($tpl_name, &$tpl_source, &$smarty)
+{
+    $ctl =& Ethna_Controller::getInstance();
+    $backend =& $ctl->getBackend();
+    $db =& $backend->getDB();
+    $sql = "SELECT source, updatetime FROM tpl WHERE name = ?";
+    $result = $db->db->getRow($sql, array($tpl_name), DB_FETCHMODE_ASSOC);
+    if ($result && !DB::isError($result)) {
+        $tpl_source = $result['source'];
+        return true;
+    } else {
+        return false;
+    }
+}
+
+function smarty_resource_db_timestamp($tpl_name, &$tpl_timestamp, &$smarty)
+{
+    $ctl =& Ethna_Controller::getInstance();
+    $backend =& $ctl->getBackend();
+    $db =& $backend->getDB();
+    $sql = "SELECT source, updatetime FROM tpl WHERE name = ?";
+    $result = $db->db->getRow($sql, array($tpl_name), DB_FETCHMODE_ASSOC);
+    if ($result && !DB::isError($result)) {
+        $tpl_timestamp = $result['updatetime'];
+        return true;
+    } else {
+        return false;
+    }
+}
+
+function smarty_resource_db_secure($tpl_name, &$smarty)
+{
+    // assume all templates are secure
+    return true;
+}
+
+function smarty_resource_db_trusted($tpl_name, &$smarty)
+{
+    // not used for templates
+}
+?>
diff -r -U2 -P sample_base/app/view/Index.php sample/app/view/Index.php
--- sample_base/app/view/Index.php      2008-06-25 14:00:27.000000000 +0900
+++ sample/app/view/Index.php   2008-06-25 15:02:11.000000000 +0900
@@ -25,4 +25,20 @@
     {
     }
+
+    /**
+     *  遷移名に対応する画面を出力する
+     *
+     *  特殊な画面を表示する場合を除いて特にオーバーライドする必要は無い
+     *  (preforward()のみオーバーライドすれば良い)
+     *
+     *  @access public
+     */
+    function forward()
+    {
+        $renderer =& $this->_getRenderer();
+        $this->_setDefault($renderer);
+        $this->forward_path = 'db:'. basename($this->forward_path, '.tpl');
+        $renderer->engine->display($this->forward_path);
+    }
 }
 ?>
diff -r -U2 -P sample_base/etc/sample-ini.php sample/etc/sample-ini.php
--- sample_base/etc/sample-ini.php      2008-06-25 14:00:27.000000000 +0900
+++ sample/etc/sample-ini.php   2008-06-25 22:18:35.000000000 +0900
@@ -15,5 +15,5 @@
     // db
     // sample-1: single db
-    // 'dsn' => 'mysql://user:password@server/database',
+    'dsn' => 'mysql://user:password@server/database',
     //
     // sample-2: single db w/ multiple users