デザイナーでも作れるスマートWebアプリ その3

Smartyでスマートプログラミング〜
デザイナーでも作れるスマートWebアプリ - maru.cc@はてな
デザイナーでも作れるスマートWebアプリ その2 - maru.cc@はてな」の続きだよ


そろそろエセ外人の売り込みっぽい口調も限界なので普通に。


このシリーズは、1000人カンファレンスUstのIRCのログの以下のコメントで思いついたエントリでした。

16:41:02 <suztomo> そこでPHPはテンプレートエンジンですよ。

16:41:22 <hayamiz> テンプレートエンジン上でテンプレートエンジン

テンプレートエンジンと言われるphpの上の、テンプレートエンジンのSmartyの上でテンプレートエンジンを動かすとか。


前回は、Flexyを使いましたが、他のテンプレートエンジンでもやってみます。
あくまで、ネタですからね。

PHPTALでやってみる

■index.php
<?php
define('BASE', dirname(dirname(__FILE__)));

ini_set('include_path'
    , BASE.'/libs'
     .PATH_SEPARATOR.ini_get('include_path')
    );

require_once('Smarty/Smarty.class.php');
$smarty = new Smarty();
$smarty->template_dir = BASE . '/app/';
$smarty->compile_dir  = BASE . '/templates_c/';
$smarty->config_dir   = BASE . '/configs/';
$smarty->cache_dir    = BASE . '/cache/';

$act = $_REQUEST['act'];
if (is_array($act)) $act = key($act);
if (!$act) $act = 'index';
$act = strtolower(str_replace('_', '/', basename($act))) . '.tpl';
if (!$smarty->template_exists($act)) $act = 'notfound.tpl';

require_once 'DB.php';
$db = DB::connect('mysql://USERNAME:PASSWORD@HOSTNAME/DBNAME');
$smarty->assign_by_ref('db', $db);

require_once 'PHPTAL.php';
$tal = new PHPTAL();
$smarty->assign_by_ref('tal', $tal);

$smarty->display($act);
Smartyのindex.tpl、notfound.tpl
{assign var='template' value=$smarty.const.BASE|cat:'/templates/'|cat:$smarty.template}
{assign var='result' value=$tal->setTemplate($template)}
{$tal->execute()}
Smartyのbbs.tpl
{strip}
{if !$db}
    {assign var='template' value=$smarty.const.BASE|cat:'/templates/dberror.tpl'}
{else}
    {assign var='list' value=$db->getAssoc('SELECT id,message,name,DATE_FORMAT(posttime,"%Y-%m-%d %H:%i:%s") AS posttime FROM bbs ORDER BY id DESC',true,null,$smarty.const.DB_FETCHMODE_OBJECT)}
    {if is_array($list)}
        {$tal->set('list',$list)}
        {assign var='template' value=$smarty.const.BASE|cat:'/templates/'|cat:$smarty.template}
    {else}
        {assign var='template' value=$smarty.const.BASE|cat:'/templates/dberror.tpl'}
    {/if}
{/if}
{assign var='result' value=$tal->setTemplate($template)}
{$tal->execute()}
{/strip}
■PHPTALのbbs.tpl
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
 "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>掲示板のサンプルだよ</title>
</head>
<body>
<h1>掲示板のサンプルだよ 一覧だよ</h1>
    <form action="./" method="post">
        めっせーじ<input type="text" name="message" value="" size="40" />
        なまえ<input type="text" name="name" value="" size="20" />
        <input type="submit" name="act[post]" value="投稿" />
    </form>
    <ul>
    <li tal:repeat="item list"><span tal:replace="item/message" /> [<span tal:replace="item/name" />](<span tal:replace="item/posttime" />)</li>
    </ul>
</body>
</html>
Smartyのpost.tpl
{strip}
{if !$db}
    {assign var='msg' value='DB接続エラー'}
    {assign var='template' value=$smarty.const.BASE|cat:'/templates/dberror.tpl'}
{else}
    {if !strlen($smarty.post.name) || !strlen($smarty.post.message)}
        {assign var='msg' value='めっせーじとなまえは必須だよ'}
    {else}
        {assign var='name' value=$db->quote($smarty.post.name)}
        {assign var='message' value=$db->quote($smarty.post.message)}
        {assign var='ip' value=$db->quote($smarty.server.REMOTE_ADDR)}
        {assign var='sql' value="INSERT INTO bbs (name,message,ip) VALUES (%s,%s,%s)"|sprintf:$name:$message:$ip}
        {assign var='result' value=$db->query($sql)}
        {if $result==$smarty.const.DB_OK}
            {assign var='msg' value='登録したよ'}
        {else}
            {assign var='msg' value='ごめん、登録できなかったよ'}
        {/if}
    {/if}
{assign var='template' value=$smarty.const.BASE|cat:'/templates/'|cat:$smarty.template}
{/if}
{$tal->set('msg',$msg)}
{assign var='result' value=$tal->setTemplate($template)}
{$tal->execute()}
{/strip}
■PHPTALのpost.tpl
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
 "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>掲示板のサンプルだよ</title>
</head>
<body>
<h1>掲示板のサンプルだよ</h1>
<div>
    <a href="./?act=bbs">一覧に戻るよ</a>
</div>
<span tal:replace="msg" />
</body>
</html>

Zoganでやってみる

■index.php
<?php
define('BASE', dirname(dirname(__FILE__)));

ini_set('include_path'
    , BASE.'/libs'
     .PATH_SEPARATOR.ini_get('include_path')
    );

require_once('Smarty/Smarty.class.php');
$smarty = new Smarty();
$smarty->template_dir = BASE . '/app/';
$smarty->compile_dir  = BASE . '/templates_c/';
$smarty->config_dir   = BASE . '/configs/';
$smarty->cache_dir    = BASE . '/cache/';

$act = $_REQUEST['act'];
if (is_array($act)) $act = key($act);
if (!$act) $act = 'index';
$act = strtolower(str_replace('_', '/', basename($act))) . '.tpl';
if (!$smarty->template_exists($act)) $act = 'notfound.tpl';

// PEAR_DB
require_once 'DB.php';
$db = DB::connect('mysql://USERNAME:PASSWORD@HOSTNAME/DBNAME');
$smarty->assign_by_ref('db', $db);

// Zogan
require_once 'XML/Template/zogan.php';
class ZoganEx extends Zogan {
    /**
     * sets value
     */
    function set($key, $value)
    {
        $this->_data[$key] = $value;
    }
}
$zogan = new ZoganEx();
$zogan->setErrorHandling(PEAR_ERROR_PRINT);
$zogan->setCompileDir(BASE . '/templates_c/');
$smarty->assign_by_ref('zogan', $zogan);

$smarty->display($act);

どうしても素では出来なかったので、継承してsetメソッド作ってます。

Smartyのindex.tpl、notfound.tpl
{assign var='template' value=$smarty.const.BASE|cat:'/templates/'|cat:$smarty.template}
{assign var='result' value=$zogan->setTemplateFile($template)}
{assign var='result' value=$zogan->display()}
Smartyのbbs.tpl
{strip}
{if !$db}
    {assign var='template' value=$smarty.const.BASE|cat:'/templates/dberror.tpl'}
{else}
    {assign var='list' value=$db->getAll('SELECT id,message,name,DATE_FORMAT(posttime,"%Y-%m-%d %H:%i:%s") AS posttime FROM bbs ORDER BY id DESC',null,$smarty.const.DB_FETCHMODE_ASSOC)}
    {if is_array($list)}
        {$zogan->set('list', $list)}
        {assign var='template' value=$smarty.const.BASE|cat:'/templates/'|cat:$smarty.template}
    {else}
        {assign var='template' value=$smarty.const.BASE|cat:'/templates/dberror.tpl'}
    {/if}
{/if}
{assign var='result' value=$zogan->setTemplateFile($template)}
{assign var='result' value=$zogan->display()}
{/strip}
■Zoganのbbs.tpl
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
 "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>掲示板のサンプルだよ</title>
</head>
<body>
<h1>掲示板のサンプルだよ 一覧だよ</h1>
    <form action="./" method="post">
        めっせーじ<input type="text" name="message" value="" size="40" />
        なまえ<input type="text" name="name" value="" size="20" />
        <input type="submit" name="act[post]" value="投稿" />
    </form>
    <ul>
    <li var:cont="{list}"><span var:cont="{message}" /> [<span var:cont="{name}" />](<span var:cont="{posttime}" />)</li>
    </ul>
</body>
</html>
Smartyのpost.tpl
{strip}
{if !$db}
    {assign var='msg' value='DB接続エラー'}
    {assign var='template' value=$smarty.const.BASE|cat:'/templates/dberror.tpl'}
{else}
    {if !strlen($smarty.post.name) || !strlen($smarty.post.message)}
        {assign var='msg' value='めっせーじとなまえは必須だよ'}
    {else}
        {assign var='name' value=$db->quote($smarty.post.name)}
        {assign var='message' value=$db->quote($smarty.post.message)}
        {assign var='ip' value=$db->quote($smarty.server.REMOTE_ADDR)}
        {assign var='sql' value="INSERT INTO bbs (name,message,ip) VALUES (%s,%s,%s)"|sprintf:$name:$message:$ip}
        {assign var='result' value=$db->query($sql)}
        {if $result==$smarty.const.DB_OK}
            {assign var='msg' value='登録したよ'}
        {else}
            {assign var='msg' value='ごめん、登録できなかったよ'}
        {/if}
    {/if}
{assign var='template' value=$smarty.const.BASE|cat:'/templates/'|cat:$smarty.template}
{/if}
{$zogan->set('msg',$msg)}
{assign var='result' value=$zogan->setTemplateFile($template)}
{assign var='result' value=$zogan->display()}
{/strip}
■Zoganのpost.tpl
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
 "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>掲示板のサンプルだよ</title>
</head>
<body>
<h1>掲示板のサンプルだよ</h1>
<div>
    <a href="./?act=bbs">一覧に戻るよ</a>
</div>
<span var:cont="{msg}" />
</body>
</html>

さいごに

Smartyは超強力ですが、使いどころを誤ると、せっかくテンプレートエンジンを導入している良さが失われてしまうと思います。


個人的なSmartyの良さは、簡単にプラグイン拡張が出来ることでしょうか。
データソース元をDBにしたり、DBからデータ取得をする拡張プラグイン関数を作ったりとか。


どちらにしても、プログラマ寄りなテンプレートエンジンなので、デザイナーと一緒にやるのならば、使うタグなり、使い方のルールが必要だと思ったりします。