剛開始接觸模版引擎的 PHP 設計師
聽到 Smarty 時
都會覺得很難
其實筆者也不例外
碰都不敢碰一下
但是後來在剖析 XOOPS 的程序架構時
開始發現 Smarty 其實並不難
只要將 Smarty 基礎功練好
在一般應用上就已經相當足夠了
當然基礎能打好
後面的進階應用也就不用怕了
這篇文章的主要用意並非要深入探討 Smarty 的使用
這在官方使用說明中都已經寫得很完整了
筆者僅在此寫下一些自己使用上的心得
讓想要了解 Smarty 卻不得其門而入的朋友
可以從中得到一些啟示
就因為這篇文章的內容不是非常深入
會使用 Smarty 的朋友們可能會覺得簡單了點
目前本文已經第三次修訂了
本想多加一些料進來
不過礙於時間的關系
很多 Smarty 的進階技巧筆者並沒有研究得很透徹
所以也不敢拿出來現眼
但筆者相信這篇文章應該能夠滿足大多數想學習 Smarty 的初學者了
當然本文有謬誤的地方也歡迎告知
筆者會在下一次的修訂中更正的
Smarty介紹
什麼是模版引擎 不知道從什麼時候開始
有人開始對 HTML 內嵌入 Server Script 覺得不太滿意
然而不論是微軟的 ASP 或是開放源碼的 PHP
都是屬於內嵌 Server Script 的網頁伺服端語言
因此也就有人想到
如果能把程序應用邏輯 (或稱商業應用邏輯) 與網頁呈現 (Layout) 邏輯分離的話
是不是會比較好呢?
其實這個問題早就存在已久
從交互式網頁開始風行時
不論是 ASP 或是 PHP 的使用者都是身兼程序開發者與視覺設計師兩種身份
可是通常這些使用者不是程序強就是美工強
如果要兩者同時兼顧
那可得死掉不少腦細胞
所以模版引擎就應運而生啦!模版引擎的目的
就是要達到上述提到的邏輯分離的功能
它能讓程序開發者專注於資料的控制或是功能的達成
而視覺設計師則可專注於網頁排版
讓網頁看起來更具有專業感!因此模版引擎很適合公司的網站開發團隊使用
使每個人都能發揮其專長!
就筆者接觸過的模版引擎來說
依資料呈現方式大概分成
需搭配程序處理的模版引擎和完全由模版本身自行決定的模版引擎兩種形式
在需搭配程序處理的模版引擎中
程序開發者必須要負責變量的呈現邏輯
也就是說他必須把變量的內容在輸出到模版前先處理好
才能做 assign 的工作
換句話說
程序開發者還是得多寫一些程序來決定變量呈現的風貌
而完全由模版本身自行決定的模版引擎
它允許變量直接 assign 到模版中
讓視覺設計師在設計模版時再決定變量要如何呈現
因此它就可能會有另一套屬於自己的模版程序語法 (如 Smarty)
以方便控制變量的呈現
但這樣一來
視覺設計師也得學習如何使用模版語言
模版引擎的運作原理
首先我們先看看以下的運行圖
一般的模版引擎 (如 PHPLib) 都是在建立模版對象時取得要解析的模版
然後把變量套入後
透過 parse() 這個方法來解析模版
最後再將網頁輸出
對 Smarty 的使用者來說
程序裡也不需要做任何 parse 的動作了
這些 Smarty 自動會幫我們做
而且已經編譯過的網頁
如果模版沒有變動的話
Smarty 就自動跳過編譯的動作
直接執行編譯過的網頁
以節省編譯的時間
使用Smarty的一些概念 在一般模版引擎中
我們常看到區域的觀念
所謂區塊大概都會長成這樣
<!
START : Block name
>
區域內容
<!
END : Block name
>
這些區塊大部份都會在 PHP 程序中以 if 或 for
while 來控制它們的顯示狀態
雖然模版看起來簡潔多了
但只要一換了顯示方式不同的模版
PHP 程序勢必要再改一次!
在 Smarty 中
一切以變量為主
所有的呈現邏輯都讓模版自行控制
因為 Smarty 會有自己的模版語言
所以不管是區塊是否要顯示還是要重復
都是用 Smarty 的模版語法 (if
foreach
section) 搭配變量內容作呈現
這樣一來感覺上好象模版變得有點復雜
但好處是只要規劃得當
PHP 程序一行都不必改
由上面的說明
我們可以知道使用Smarty 要掌握一個原則
將程序應用邏輯與網頁呈現邏輯明確地分離
就是說 PHP 程序裡不要有太多的 HTML 碼
程序中只要決定好那些變量要塞到模版裡
讓模版自己決定該如何呈現這些變量 (甚至不出現也行)
Smarty的基礎
安裝Smarty 首先
我們先決定程序放置的位置
Windows下可能會類似這樣的位置
「 d:\appserv\web\demo\ 」
Linux下可能會類似這樣的位置
「 /home/jaceju/public_html/ 」
到Smarty的官方網站下載最新的Smarty套件
解開 Smarty
後
會看到很多檔案
其中有個 libs 資料夾
在 libs 中應該會有
個 class
php 檔 +
個 debug
tpl +
個 plugin 資料夾 +
個 core 資料夾
然後直接將 libs 復制到您的程序主資料夾下
再更名為 class 就可以了
就這樣?沒錯!這種安裝法比較簡單
適合一般沒有自己主機的使用者
至於 Smarty 官方手冊中為什麼要介紹一些比較復雜的安裝方式呢?基本上依照官方的方式安裝
可以只在主機安裝一次
然後提供給該主機下所有設計者開發不同程序時直接引用
而不會重復安裝太多的 Smarty 復本
而筆者所提供的方式則是適合要把程序帶過來移過去的程序開發者使用
這樣不用煩惱主機有沒有安裝 Smarty
程序的資料夾設定 以筆者在Windows安裝Appserv為例
程序的主資料夾是「d:\appserv\web\demo\」
安裝好Smarty後
我們在主資料夾下再建立這樣的資料夾
educitycn/img_///gif > 在 Linux 底下
請記得將 templates_c 的權限變更為
Windows 下則將其只讀取消
第一個用Smarty寫的小程序 我們先設定 Smarty 的路徑
請將以下這個檔案命名為 main
php
並放置到主資料夾下
main
php:
<?php
include
class/Smarty
class
php
;
define(
__SITE_ROOT
d:/appserv/web/demo
); // 最後沒有斜線
$tpl = new Smarty();
$tpl
>template_dir = __SITE_ROOT
/templates/
;
$tpl
>compile_dir = __SITE_ROOT
/templates_c/
;
$tpl
>config_dir = __SITE_ROOT
/configs/
;
$tpl
>cache_dir = __SITE_ROOT
/cache/
;
$tpl
>left_delimiter =
<{
;
$tpl
>right_delimiter =
}>
;
?>
照上面方式設定的用意在於
程序如果要移植到其它地方
只要改 __SITE_ROOT 就可以啦
(這裡是參考 XOOPS 的 )
Smarty 的模版路徑設定好後
程序會依照這個路徑來抓所有模版的相對位置 (范例中是
d:/appserv/web/demo/templates/
)
然後我們用 display() 這個 Smarty 方法來顯示我們的模版
接下來我們在 templates 資料夾下放置一個
(擴展名叫什麼都無所謂
但便於視覺設計師開發
筆者都還是以 為主
)
templates/:
<html>
<head>
<meta http
equiv=
Content
Type
content=
text/html; charset=big
>
<title><{$title}></title>
</head>
<body>
<{$content}>
</body>
</html>
現在我們要將上面的模版顯示出來
並將網頁標題 ($title) 與內容 ($content) 更換
請將以下檔案內容命名為 test
php
並放置在主資料夾下
test
php:
<?php
require
main
php
;
$tpl
>assign(
title
測試用的網頁標題
);
$tpl
>assign(
content
測試用的網頁內容
);
// 上面兩行也可以用這行代替
// $tpl
>assign(array(
title
=>
測試用的網頁標題
content
=>
測試用的網頁內容
));
$tpl
>display(
);
?>
請打開浏覽器
輸入 試試看(依您的環境決定網址)
應該會看到以下的畫面
educitycn/img_///gif > 再到 templates_c 底下
我們會看到一個奇怪的資料夾 (%%
)
再點選下去也是一個奇怪的資料夾 (%%
)
而其中有一個檔案
templates_c/%%
/%%
/
php:
<?php /* Smarty version
created on
:
:
compiled from */ ?>
<html>
<head>
<meta http
equiv=
Content
Type
content=
text/html; charset=big
>
<title><?php echo $this
>_tpl_vars[
title
]; ?></title>
</head>
<body>
<?php echo $this
>_tpl_vars[
content
]; ?>
</body>
</html>
沒錯
這就是 Smarty 編譯過的檔案
它將我們在模版中的變量轉換成了 PHP 的語法來執行
下次再讀取同樣的內容時
Smarty 就會直接抓取這個檔案來執行了
最後我們整理一下整個 Smarty 程序撰寫步驟
Step
加載 Smarty 模版引擎
Step
建立 Smarty 對象
Step
設定 Smarty 對象的參數
Step
在程序中處理變量後
再用 Smarty 的 assign 方法將變量置入模版裡
Step
利用 Smarty 的 display 方法將網頁秀出
如何安排你的程序架構 上面我們看到除了 Smarty 所需要的資料夾外 (class
configs
templates
templates_c)
還有兩個資料夾
includes
modules
其實這是筆者模仿 XOOPS 的架構所建立出來的
因為 XOOPS 是筆者所接觸到的程序中
少數使用 Smarty 模版引擎的架站程序
所謂西瓜偎大邊
筆者這樣的程序架構雖沒有 XOOPS 的百分之一強
但至少給人看時還有 XOOPS 撐腰
includes 這個資料夾主要是用來放置一些 function
sql 檔
這樣在 main
php 就可以將它們引入了
如下
main
php:
<?php
include
class/Smarty
class
php
;
define(
__SITE_ROOT
d:/appserv/web/demo
); // 最後沒有斜線
// 以 main
php 的位置為基准
require_once
includes/functions
php
;
require_once
includes/include
php
;
$tpl = new Smarty();
$tpl
>template_dir = __SITE_ROOT
/templates/
;
$tpl
>compile_dir = __SITE_ROOT
/templates_c/
;
$tpl
>config_dir = __SITE_ROOT
/configs/
;
$tpl
>cache_dir = __SITE_ROOT
/cache/
;
$tpl
>left_delimiter =
<{
;
$tpl
>right_delimiter =
}>
;
?>
modules 這個資料夾則是用來放置程序模塊的
如此一來便不會把程序丟得到處都是
整體架構一目了然
上面我們也提到 main
php
這是整個程序的主要核心
不論是常數定義
外部程序加載
共享變量建立等
都是在這裡開始的
所以之後的模塊都只要將這個檔案包含進來就可以啦
因此在程序流程規劃期間
就必須好好構思 main
php 中應該要放那些東西
當然利用 include 或 require 指令
把每個環節清楚分離是再好不過了
educitycn/img_///gif > 在上節提到的 Smarty 程序
步驟
main
php 就會幫我們先將前
個步驟做好
後面的模塊程序只要做後面兩個步驟就可以了
從變量開始 如何使用變量
從上一章范例中
我們可以清楚地看到我們利用 <{ 及 }> 這兩個標示符號將變量包起來
預設的標示符號為 { 及 }
但為了中文沖碼及 Javascript 的關系
因此筆者還是模仿 XOOPS
將標示符號換掉
變量的命名方式和 PHP 的變量命名方式是一模一樣的
前面也有個 $ 字號 (這和一般的模版引擎不同)
標示符號就有點像是 PHP 中的 <?php 及 ?> (事實上它們的確會被替換成這個)
所以以下的模版變量寫法都是可行的
<{$var}>
<{ $var }> <!
和變量之間有空格
>
<{$var
}> <!
啟始的標示符號和結束的標示符號不在同一行
>
在 Smarty 裡
變量預設是全域的
也就是說你只要指定一次就好了
指定兩次以上的話
變量內容會以最後指定的為主
就算我們在主模版中加載了外部的子模版
子模版中同樣的變量一樣也會被替代
這樣我們就不用再針對子模版再做一次解析的動作
而在 PHP 程序中
我們用 Smarty 的 assign 來將變量置放到模版中
assign 的用法官方手冊中已經寫得很多了
用法就如同上一節的范例所示
不過在重復區塊時
我們就必須將變量做一些手腳後
才能將變量 assign 到模版中
這在下一章再提
修飾你的變量 上面我們提到 Smarty 變量呈現的風貌是由模版自行決定的
所以 Smarty 提供了許多修飾變量的函式
使用的方法如下
<{變量|修飾函式}> <!
當修飾函式沒有參數時
>
<{變量|修飾函式:
參數(非必要
視函式而定)
}> <!
當修飾函式有參數時
>
范例如下
<{$var|nl
br}> <!
將變量中的換行字符換成 <br />
>
<{$var|string_format:
%
d
}> <!
將變量格式化
>
好
那為什麼要讓模版自行決定變量呈現的風貌?先看看底下的 HTML
這是某個購物車結帳的部份畫面
<input name=
total
type=
hidden
value=
/>
總金額
元
一般模版引擎的模版可能會這樣寫
<input name=
total
type=
hidden
value=
{total}
/>
總金額
{format_total} 元
它們的 PHP 程序中要這樣寫
<?php
$total =
;
$tpl
>assign(
total
$total);
$tpl
>assign(
format_total
number_format($total));
?>
而 Smarty 的模版就可以這樣寫
(number_format 修飾函式請到Smarty 官方網頁下載)
<input name=
total
type=
hidden
value=
<{$total}>
/>
總金額
<{$total|number_format:
}> 元
Smarty 的 PHP 程序中只要這樣寫
<?php
$total =
;
$tpl
>assign(
total
$total);
?>
所以在 Smarty 中我們只要指定一次變量
剩下的交給模版自行決定即可
這樣了解了嗎?這就是讓模版自行決定變量呈現風貌的好處!
控制模版的內容
重復的區塊 在 Smarty 樣板中
我們要重復一個區塊有兩種方式
foreach 及 section
而在程序中我們則要 assign 一個數組
這個數組中可以包含數組數組
就像下面這個例子
首先我們來看 PHP 程序是如何寫的
test
php:
<?php
require
main
php
;
$array
= array(
=>
蘋果
=>
菠蘿
=>
香蕉
=>
芭樂
);
$tpl
>assign(
array
$array
);
$array
= array(
array(
index
=>
data
index
=>
data
index
=>
data
)
array(
index
=>
data
index
=>
data
index
=>
data
)
array(
index
=>
data
index
=>
data
index
=>
data
)
array(
index
=>
data
index
=>
data
index
=>
data
)
array(
index
=>
data
index
=>
data
index
=>
data
));
$tpl
>assign(
array
$array
);
$tpl
>display(
);
?>
而模版的寫法如下
templates/:
<html>
<head>
<meta http
equiv=
Content
Type
content=
text/html; charset=big
>
<title>測試重復區塊</title>
</head>
<body>
<pre>
利用 foreach 來呈現 array
<{foreach item=item
from=$array
}>
<{$item
}>
<{/foreach}>
利用 section 來呈現 array
<{section name=sec
loop=$array
}>
<{$array
[sec
]}>
<{/section}>
利用 foreach 來呈現 array
<{foreach item=index
from=$array
}>
<{foreach key=key
item=item
from=$index
}>
<{$key
}>: <{$item
}>
<{/foreach}>
<{/foreach}>
利用 section 來呈現 array
<{section name=sec
loop=$array
}>
index
: <{$array
[sec
]
index
}>
index
: <{$array
[sec
]
index
}>
index
: <{$array
[sec
]
index
}>
<{/section}>
</pre>
</body>
</html>
執行上例後
我們發現不管是 foreach 或 section 兩個執行結果是一樣的
那麼兩者到底有何不同呢?
第一個差別很明顯
就是foreach 要以巢狀處理的方式來呈現我們所 assign 的兩層數組變量
而 section 則以「主數組[循環名稱]
子數組索引」即可將整個數組呈現出來
由此可知
Smarty 在模版中的 foreach 和 PHP 中的 foreach 是一樣的
而 section 則是 Smarty 為了處理如上列的數組變量所發展出來的敘述
當然 section 的功能還不只如此
除了下一節所談到的巢狀資料呈現外
官方手冊中也提供了好幾個 section 的應用范例
不過要注意的是
丟給 section 的數組索引必須是從
開始的正整數
即
如果您的數組索引不是從
開始的正整數
那麼就得改用 foreach 來呈現您的資料
您可以參考官方討論區中的此篇討論
其中探討了 section 和 foreach 的用法
巢狀資料的呈現 模版引擎裡最令人傷腦筋的大概就是巢狀資料的呈現吧
許多著名的模版引擎都會特意強調這點
不過這對 Smarty 來說卻是小兒科
最常見到的巢狀資料
就算論譠程序中的討論主題區吧
假設要呈現的結果如下
公告區
站務公告
文學專區
好書介紹
奇文共賞
計算機專區
硬件外圍
軟件討論
程序中我們先以靜態資料為例
test
php:
<?php
require
main
php
;
$forum = array(
array(
category_id
=>
category_name
=>
公告區
topic
=> array(
array(
topic_id
=>
topic_name
=>
站務公告
)
)
)
array(
category_id
=>
category_name
=>
文學專區
topic
=> array(
array(
topic_id
=>
topic_name
=>
好書介紹
)
array(
topic_id
=>
topic_name
=>
奇文共賞
)
)
)
array(
category_id
=>
category_name
=>
計算機專區
topic
=> array(
array(
topic_id
=>
topic_name
=>
硬件外圍
)
array(
topic_id
=>
topic_name
=>
軟件討論
)
)
)
);
$tpl
>assign(
forum
$forum);
$tpl
>display(
);
?>
模版的寫法如下
templates/:
<html>
<head>
<title>巢狀循環測試</title>
</head>
<body>
<table width=
border=
align=
center
cellpadding=
cellspacing=
>
<{section name=sec
loop=$forum}>
<tr>
<td colspan=
><{$forum[sec
]
category_name}></td>
</tr>
<{section name=sec
loop=$forum[sec
]
topic}>
<tr>
<td width=
> </td>
<td width=
><{$forum[sec
]
topic[sec
]
topic_name}></td>
</tr>
<{/section}>
<{/section}>
</table>
</body>
</html>
執行的結果就像筆者舉的例子一樣
因此呢
在程序中我們只要想辦法把所要重復值一層一層的塞到數組中
再利用 <{第一層數組[循環
]
第二層數組[循環
]
第三層數組[循環
]
數組索引}> 這樣的方式來顯示每一個巢狀循環中的值
至於用什麼方法呢?下一節使用數據庫時我們再提
轉換數據庫中的資料 上面提到如何顯示巢狀循環
而實際上應用時我們的資料可能是從數據庫中抓取出來的
所以我們就得想辦法把數據庫的資料變成上述的多重數組的形式
這裡筆者用一個 DB 類別來抓取數據庫中的資料
您可以自行用您喜歡的方法
我們只修改 PHP 程序
模版還是上面那個 (這就是模版引擎的好處~)
其中 $db 這個對象假設已經在 main
php 中建立好了
而且抓出來的資料就是上面的例子
test
php:
<?php
require
main
php
;
// 先建立第一層數組
$category = array();
$db
>setSQL($SQL
CATEGORY
);
if (!$db
>query(
CATEGORY
)) die($db
>error());
// 抓取第一層循環的資料
while ($item_category = $db
>fetchAssoc(
CATEGORY
))
{
// 建立第二層數組
$topic = array();
$db
>setSQL(sprintf($SQL
$item_category[
category_id
])
TOPIC
);
if (!$db
>query(
TOPIC
)) die($db
>error());
// 抓取第二層循環的資料
while ($item_topic = $db
>fetchAssoc(
TOPIC
))
{
// 把抓取的數據推入第二層數組中
array_push($topic
$item_topic);
}
// 把第二層數組指定為第一層數組所抓取的數據中的一個成員
$item_category[
topic
] = $topic;
// 把第一層數據推入第一層數組中
array_push($category
$item_category);
}
$tpl
>assign(
forum
$category);
$tpl
>display(
);
?>
在數據庫抓取一筆資料後
我們得到的是一個包含該筆數據的數組
透過 while 敘述及 array_push 函式
我們將數據庫中的資料一筆一筆塞到數組裡
如果您只用到單層循環
就把第二層循環 (紅色的部份) 去掉即可
決定內容是否顯示 要決定是否顯示內容
我們可以使用 if 這個語法來做選擇
例如如果使用者已經登入的話
我們的模版就可以這樣寫
<{if $is_login == true}>
顯示使用者操作選單
<{else}>
顯示輸入帳號和密碼的窗體
<{/if}>
要注意的是
「==」號兩邊一定要各留至少一個空格符
否則 Smarty 會無法解析
if 語法一般的應用可以參照官方使用說明
所以筆者在這裡就不詳加介紹了
不過筆者發現了一個有趣的應用
常常會看到程序裡要產生這樣的一個表格
(數字代表的是資料集的順序)
這個筆者稱之為「橫向重復表格」
它的特色和傳統的縱向重復不同
前幾節我們看到的重復表格都是從上而下
一列只有一筆資料
而橫向重復表格則可以橫向地在一列中產生 n 筆資料後
再換下一列
直到整個循環結束
要達到這樣的功能
最簡單的方式只需要 section 和 if 搭配即可
我們來看看下面這個例子
test
php:
<?php
require
main
php
;
$my_array = array(
array(
value
=>
)
array(
value
=>
)
array(
value
=>
)
array(
value
=>
)
array(
value
=>
)
array(
value
=>
)
array(
value
=>
)
array(
value
=>
)
array(
value
=>
)
array(
value
=>
));
$tpl
>assign(
my_array
$my_array);
$tpl
>display(
);
?>
模版的寫法如下
templates/:
<html>
<head>
<title>橫向重復表格測試</title>
</head>
<body>
<table width=
border=
cellspacing=
cellpadding=
>
<tr>
<{section name=sec
loop=$my_array}>
<td><{$my_array[sec
]
value}></td>
<{if $smarty
section
sec
rownum is div by
}>
</tr>
<tr>
<{/if}>
<{/section}>
</tr>
</table>
</body>
</html>
重點在於 $smarty
section
sec
rownum 這個 Smarty 變量
在 section 循環中這個變量會取得從
開始的索引值
所以當 rownum 能被
除盡時
就輸出 </tr><tr> 使表格換列 (注意!是 </tr> 在前面<tr> 在後面)
因此數字
就是我們在一列中想要呈現的資料筆數
各位可以由此去變化其它不同的呈現方式
加載外部內容 我們可以在模版內加載 PHP 程序代碼或是另一個子模版
分別是使用 include_php 及 include 這兩個 Smarty 模版語法
include_php 筆者較少用
使用方式可以查詢官方手冊
這裡不再敘述
在使用 include 時
我們可以預先加載子模版
或是動態加載子模版
預先加載通常使用在有共同的文件標頭及版權宣告
而動態加載則可以用在統一的框架頁
而進一步達到如 Winamp 般可換 Skin
當然這兩種我們也可以混用
視狀況而定
我們來看看下面這個例子
test
php:
<?php
require
main
php
;
$tpl
>assign(
title
Include 測試
);
$tpl
>assign(
content
這是模版
中的變量
);
$tpl
>assign(
dyn_page
);
$tpl
>display(
);
?>
模版
的寫法如下
templates/:
<html>
<head>
<meta http
equiv=
Content
Type
content=
text/html; charset=big
>
<title><{$title}></title>
</head>
<body>
<{include file=
}><br />
<{include file=$dyn_page}>
<{include file=
custom_var=
自訂變量的內容
}>
</body>
</html>
模版
的寫法如下
templates/:
<{$content}>
模版
的寫法如下
templates/:
這是模版
的內容
模版
的寫法如下
templates/:
<{$custom_var}>
這裡注意幾個重點
模版的位置都是以先前定義的 template_dir 為基准
所有 include 進來的子模版中
其變量也會被解譯
include 中可以用「變量名稱=變量內容」來指定引含進來的模版中所包含的變量
如同上面模版
的做法
From:http://tw.wingwit.com/Article/program/PHP/201311/20843.html