当前位置:首页> PHP教程> php模板
关键字
文章内容
Smarty入门
 
 
修改时间:[2009/07/19 22:41]    阅读次数:[1868]    发表者:[起缘]
 
Smarty 入門

作者:Jace Ju

1  Smarty介紹
  1.1 什麼是樣版引擎
  1.2 樣版引擎的咦髟??
  1.3 使用Smarty的一些概念
2 Smarty的基礎
  2.1 安裝Smarty
  2.2 程式的資料夾設定
  2.3 第一個用Smarty寫的小程式
  2.4 如何安排你的程式架構
3 從變數開始
  3.1 如何使用變數
  3.2 修飾你的變數
4 控制樣版的內容
  4.1 重覆的區塊
  4.2 巢狀資料的呈現
  4.3 轉換資料庫中的資料
  4.4 決定內容是否顯示
  4.5 載入外部內容


Smarty 入門

序言
剛開始接觸樣版引擎的 PHP 設計師,聽到 Smarty 時,都會覺得很難。其實筆者也不例外,碰都不敢碰一下。但是後來在剖析 XOOPS 的程式架構時,開始發現 Smarty 其實並不難。只要將 Smarty 基礎功練好,在一般應用上就已經相當足夠了。當然基礎能打好,後面的進階應用也就不用怕了。

這篇文章的主要用意並非要深入探討 Smarty 的使用,這在官方使用說明中都已經寫得很完整了。筆者僅在此寫下一些自己使用上的心得,讓想要了解 Smarty 卻不得其門而入的朋友,可以從中得到一些啟示。就因為這篇文章的內容不是非常深入,會使用 Smarty 的朋友們可能會覺得簡單了點。

目前本文已經第三次修訂了,本想多加一些料進來;不過礙於時間的關係,很多 Smarty 的進階技巧筆者並沒有研究得很透徹,所以也不敢拿出來現眼,但筆者相信這篇文章應該能夠滿足大多數想學習 Smarty 的初學者了。當然本文有謬誤的地方也歡迎告知,筆者會在下一次的修訂中更正的。

P.S. 請在 1024 x 768 的環境下看會比較好。

jaceju@seed.net.tw 2004/02/09



1 Smarty介紹


1.1 什麼是樣版引擎

不知道從什麼時候開始,有人開始對 HTML 內嵌入 Server Script 覺得不太滿意。然而不論是微軟的 ASP 或是開放源碼的 PHP,都是屬於內嵌 Server Script 的網頁伺服端語言。因此也就有人想到,如果能把程式應用邏輯 (或稱商業應用邏輯) 與網頁呈現 (Layout) 邏輯分離的話,是不是會比較好呢?

其實這個問題早就存在已久,從互動式網頁開始風行時,不論是 ASP 或是 PHP 的使用者都是身兼程式開發者與視覺設計師兩種身份。可是通常這些使用者不是程式強就是美工強,如果要兩者同時兼顧,那可得死掉不少腦細胞...

所以樣版引擎就應叨??玻?影嬉?娴哪康模?褪且?_到上述提到的邏輯分離的功能。它能讓程式開發者專注於資料的控制或是功能的達成;而視覺設計師則可專注於網頁排版,讓網頁看起來更具有專業感!因此樣版引擎很適合公司的網站開發團隊使用,使每個人都能發揮其專長!

就筆者接觸過的樣版引擎來說,依資料呈現方式大概分成:需搭配程式處理的樣版引擎和完全由樣版本身自行決定的樣版引擎兩種形式。

在需搭配程式處理的樣版引擎中,程式開發者必須要負責變數的呈現邏輯,也就是說他必須把變數的內容在輸出到樣版前先處理好,才能做 assign 的工作。換句話說,程式開發者還是得多寫一些程式來決定變數呈現的風貌。而完全由樣版本身自行決定的樣版引擎,它允許變數直接 assign 到樣版中,讓視覺設計師在設計樣版時再決定變數要如何呈現。因此它就可能會有另一套屬於自己的樣版程式語法 (如 Smarty) ,以方便控制變數的呈現。但這樣一來,視覺設計師也得學習如何使用樣版語言。



1.2 樣版引擎的咦髟??
首先我們先看看以下的咦鲌D:

一般的樣版引擎 (如 PHPLib) 都是在建立樣版物件時取得要解析的樣版,然後把變數套入後,透過 parse() 這個方法來解析樣版,最後再將網頁輸出。

對 Smarty 的使用者來說,程式裡也不需要做任何 parse 的動作了,這些 Smarty 自動會幫我們做。而且已經編譯過的網頁,如果樣版沒有變動的話, Smarty 就自動跳過編譯的動作,直接執行編譯過的網頁,以節省編譯的時間。

1.3 使用Smarty的一些概念
在一般樣版引擎中,我們常看到區域的觀念,所謂區塊大概都會長成這樣:

<!-- START : Block name -->
區域內容
<!-- END : Block name -->
這些區塊大部份都會在 PHP 程式中以 if 或 for, while 來控制它們的顯示狀態,雖然樣版看起來簡潔多了,但只要一換了顯示方式不同的樣版, PHP 程式勢必要再改一次!

在 Smarty 中,一切以變數為主,所有的呈現邏輯都讓樣版自行控制。因為 Smarty 會有自己的樣版語言,所以不管是區塊是否要顯示還是要重覆,都是用 Smarty 的樣版語法 (if, foreach, section) 搭配變數內容作呈現。這樣一來感覺上好像樣版變得有點複雜,但好處是只要規劃得當, PHP 程式一行都不必改。

由上面的說明,我們可以知道使用 Smarty 要掌握一個原則:將程式應用邏輯與網頁呈現邏輯明確地分離。就是說 PHP 程式裡不要有太多的 HTML 碼。程式中只要決定好那些變數要塞到樣版裡,讓樣版自己決定該如何呈現這些變數 (甚至不呈現也行) 。

2 Smarty的基礎

2.1 安裝Smarty

首先,我們先決定程式放置的位置。
Windows下可能會類似這樣的位置:「 d:appservwebdemo 」。
Linux下可能會類似這樣的位置:「 /home/jaceju/public_html/ 」。
到Smarty的官方網站下載最新的Smarty套件:http://smarty.php.net。
解開 Smarty 2.6.0 後,會看到很多檔案,其中有個 libs 資料夾。在 libs 中應該會有 3 個 class.php 檔 + 1 個 debug.tpl + 1 個 plugin 資料夾 + 1 個 core 資料夾。然後直接將 libs 複製到您的程式主資料夾下,再更名為 class 就可以了。就這樣?沒錯!這種安裝法比較簡單,適合一般沒有自己主機的使用者。

至於 Smarty 官方手冊中為什麼要介紹一些比較複雜的安裝方式呢?基本上依照官方的方式安裝,可以只在主機安裝一次,然後提供給該主機下所有設計者開發不同程式時直接引用,而不會重覆安裝太多的 Smarty 複本。而筆者所提供的方式則是適合要把程式帶過來移過去的程式開發者使用,這樣不用煩惱主機有沒有安裝 Smarty 。

2.2 程式的資料夾設定
以筆者在Windows安裝Appserv為例,程式的主資料夾是「d:appservwebdemo」。安裝好Smarty後,我們在主資料夾下再建立這樣的資料夾:

在 Linux 底下,請記得將 templates_c 的權限變更為 777 。Windows 下則將其唯讀取消。

2.3 第一個用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 '}>'
?> 


(非常全面的一个php技术网站, 有相当丰富的文章和源代码.)
照上面方式設定的用意在於,程式如果要移植到其他地方,只要改 __SITE_ROOT 就可以啦。 (這裡是參考 XOOPS 的 )

Smarty 的樣版路徑設定好後,程式會依照這個路徑來抓所有樣版的相對位置 (範例中是 'd:/appserv/web/demo/templates/' ) 。然後我們用 display() 這個 Smarty 方法來顯示我們的樣版。

接下來我們在 templates 資料夾下放置一個 test.htm:(副檔名叫什麼都無所謂,但便於視覺設計師開發,筆者都還是以 .htm 為主。)

templates/test.htm:

<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=big5">
<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('test.htm'); 
?> 


(非常全面的一个php技术网站, 有相当丰富的文章和源代码.)
請打開瀏覽器,輸入 http://localhost/demo/test.php 試試看(依您的環境決定網址),應該會看到以下的畫面:

再到 templates_c 底下,我們會看到一個奇怪的資料夾 (%%179) ,再點選下去也是一個奇怪的資料夾 (%%1798044067) ,而其中有一個檔案:

templates_c/%%179/%%1798044067/test.htm.php:
<?php/* Smarty version 2.6.0, created on 2003-12-15 22:19:45 compiled from test.htm */ ?> 

(非常全面的一个php技术网站, 有相当丰富的文章和源代码.)
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=big5">
<title><?php echo $this->_tpl_vars['title']; ?>[/code]</title>
</head>
<body>
<?php echo $this->_tpl_vars['content']; ?>[/code]
</body>
</html>
沒錯,這就是 Smarty 編譯過的檔案。它將我們在樣版中的變數轉換成了 PHP 的語法來執行,下次再讀取同樣的內容時, Smarty 就會直接抓取這個檔案來執行了。

最後我們整理一下整個 Smarty 程式撰寫步驟:

Step 1. 載入 Smarty 樣版引擎。
Step 2. 建立 Smarty 物件。
Step 3. 設定 Smarty 物件的參數。Step 4. 在程式中處理變數後,再用 Smarty 的 assign 方法將變數置入樣版裡。
Step 5. 利用 Smarty 的 display 方法將網頁秀出。


2.4 如何安排你的程式架構
上面我們看到除了 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 '}>'
?> 


(非常全面的一个php技术网站, 有相当丰富的文章和源代码.)
modules 這個資料夾則是用來放置程式模組的,如此一來便不會把程式丟得到處都是,整體架構一目瞭然。

上面我們也提到 main.php ,這是整個程式的主要核心,不論是常數定義、外部程式載入、共用變數建立等,都是在這裡開始的。所以之後的模組都只要將這個檔案包含進來就可以啦。因此在程式流程規劃期間,就必須好好構思 main.php 中應該要放那些東西;當然利用 include 或 require 指令,把每個環節清楚分離是再好不過了。

在上節提到的 Smarty 程式 5 步驟, main.php 就會幫我們先將前 3 個步驟做好,後面的模組程式只要做後面兩個步驟就可以了。

3 從變數開始

3.1 如何使用變數

從上一章範例中,我們可以清楚地看到我們利用 <{ 及 }> 這兩個標示符號將變數包起來。預設的標示符號為 { 及 } ,但為了中文衝碼及 Javascript 的關係,因此筆者還是模仿 XOOPS ,將標示符號換掉。變數的命名方式和 PHP 的變數命名方式是一模一樣的,前面也有個 $ 字號 (這和一般的樣版引擎不同)。標示符號就有點像是 PHP 中的 <?php 及 ?>[/code] (事實上它們的確會被替換成這個) ,所以以下的樣版變數寫法都是可行的:

1. <{$var}>
2. <{ $var }> <!-- 和變數之間有空格 -->
3. <{$var}> <!-- 啟始的標示符號和結束的標示符號不在同一行 -->  

在 Smarty 裡,變數預設是全域的,也就是說你只要指定一次就好了。指定兩次以上的話,變數內容會以最後指定的為主。就算我們在主樣版中載入了外部的子樣版,子樣版中同樣的變數一樣也會被替代,這樣我們就不用再針對子樣版再做一次解析的動作。

而在 PHP 程式中,我們用 Smarty 的 assign 來將變數置放到樣版中。 assign 的用法官方手冊中已經寫得很多了,用法就如同上一節的範例所示。不過在重覆區塊時,我們就必須將變數做一些手腳後,才能將變數 assign 到樣版中,這在下一章再提。

3.2 修飾你的變數

上面我們提到 Smarty 變數呈現的風貌是由樣版自行決定的,所以 Smarty 提供了許多修飾變數的函式。使用的方法如下:

<{變數|修飾函式}> <!-- 當修飾函式沒有參數時 -->
<{變數|修飾函式:"參數(非必要,視函式而定)"}> <!-- 當修飾函式有參數時 -->

範例如下:

<{$var|nl2br}> <!-- 將變數中的換行字元換成 <br /> -->
<{$var|string_format:"%02d"}> <!-- 將變數格式化 -->
好,那為什麼要讓樣版自行決定變數呈現的風貌?先看看底下的 HTML ,這是某個購物車結帳的部份畫面。

<input name="total" type="hidden" value="21000" />
總金額:21,000 元

一般樣版引擎的樣版可能會這樣寫:

<input name="total" type="hidden" value="{total}" />
總金額:{format_total} 元
它們的 PHP 程式中要這樣寫:
<?php
    $total 
21000
    
$tpl->assign("total"$total); 
    
$tpl->assign("format_total"number_format($total)); 
?> 


(非常全面的一个php技术网站, 有相当丰富的文章和源代码.)
而 Smarty 的樣版就可以這樣寫: (number_format 修飾函式請到 Smarty 官方網頁下載)


<input name="total" type="hidden" value="<{$total}>" />
總金額:<{$total|number_format:""}> 元
Smarty 的 PHP 程式中只要這樣寫:
<?php
    $total 
21000
    
$tpl->assign("total"$total); 
?> 


(非常全面的一个php技术网站, 有相当丰富的文章和源代码.)
所以在 Smarty 中我們只要指定一次變數,剩下的交給樣版自行決定即可。這樣瞭解了嗎?這就是讓樣版自行決定變數呈現風貌的好處!

4 控制樣版的內容

4.1 重覆的區塊

在 Smarty 樣板中,我們要重覆一個區塊有兩種方式: foreach 及 section 。而在程式中我們則要 assign 一個陣列,這個陣列中可以包含數組陣列。就像下面這個例子:

首先我們來看 PHP 程式是如何寫的:

test2.php:
<?php
    
require "main.php"
    
$array1 = array(=> "蘋果"=> "鳳梨"=> "香蕉"=> "芭樂"); 
    
$tpl->assign("array1"$array1); 
    
$array2 = array( 
    array(
"index1" => "data1-1""index2" => "data1-2""index3" => "data1-3"), 
    array(
"index1" => "data2-1""index2" => "data2-2""index3" => "data2-3"), 
    array(
"index1" => "data3-1""index2" => "data3-2""index3" => "data3-3"), 
    array(
"index1" => "data4-1""index2" => "data4-2""index3" => "data4-3"), 
    array(
"index1" => "data5-1""index2" => "data5-2""index3" => "data5-3")); 
    
$tpl->assign("array2"$array2); 
    
$tpl->display("test2.htm"); 
?> 


(非常全面的一个php技术网站, 有相当丰富的文章和源代码.)
而樣版的寫法如下:

templates/test2.htm:

<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=big5">
<title>測試重覆區塊</title>
</head>
<body>
<pre>
利用 foreach 來呈現 array1
<{foreach item=item1 from=$array1}>
<{$item1}>
<{/foreach}>
利用 section 來呈現 array1
<{section name=sec1 loop=$array1}>
<{$array1[sec1]}>
<{/section}>
利用 foreach 來呈現 array2
<{foreach item=index2 from=$array2}>
<{foreach key=key2 item=item2 from=$index2}>
<{$key2}>: <{$item2}>
<{/foreach}>
<{/foreach}>
利用 section 來呈現 array1
<{section name=sec2 loop=$array2}>
index1: <{$array2[sec2].index1}>
index2: <{$array2[sec2].index2}>
index3: <{$array2[sec2].index3}>
<{/section}>
</pre>
</body>
</html>
執行上例後,我們發現不管是 foreach 或 section 兩個執行結果是一樣的。那麼兩者到底有何不同呢?

第一個差別很明顯,就是 foreach 要以巢狀處理的方式來呈現我們所 assign 的兩層陣列變數,而 section 則以「主陣列[迴圈名稱].子陣列索引」即可將整個陣列呈現出來。由此可知, Smarty 在樣版中的 foreach 和 PHP 中的 foreach 是一樣的;而 section 則是 Smarty 為了處理如上列的陣列變數所發展出來的敘述。當然 section 的功能還不只如此,除了下一節所談到的巢狀資料呈現外,官方手冊中也提供了好幾個 section 的應用範例。

不過要注意的是,丟給 section 的陣列索引必須是從 0 開始的正整數,即 0, 1, 2, 3, ...。如果您的陣列索引不是從 0 開始的正整數,那麼就得改用 foreach 來呈現您的資料。您可以參考官方討論區中的此篇討論,其中探討了 section 和 foreach 的用法。

4.2 巢狀資料的呈現

樣版引擎裡最令人傷腦筋的大概就是巢狀資料的呈現吧,許多著名的樣版引擎都會特意強調這點,不過這對 Smarty 來說卻是小兒科。

最常見到的巢狀資料,就算論譠程式中的討論主題區吧。假設要呈現的結果如下:

  公告區
     站務公告
  文學專區
     好書介紹
     奇文共賞
  電腦專區
     硬體週邊
     軟體討論

程式中我們先以靜態資料為例:

test3.php:
<?php
    
require "main.php"
    
$forum = array( 
        array(
"category_id" => 1"category_name" => "公告區"
            
"topic" => array( 
                array(
"topic_id" => 1"topic_name" => "站務公告"
            ) 
        ), 
        array(
"category_id" => 2"category_name" => "文學專區"
            
"topic" => array( 
                array(
"topic_id" => 2"topic_name" => "好書介紹"), 
                array(
"topic_id" => 3"topic_name" => "奇文共賞"
            ) 
        ), 
        array(
"category_id" => 3"category_name" => "電腦專區"
            
"topic" => array( 
                array(
"topic_id" => 4"topic_name" => "硬體週邊"), 
                array(
"topic_id" => 5"topic_name" => "軟體討論"
            ) 
        ) 
    ); 
    
$tpl->assign("forum"$forum); 
    
$tpl->display("test3.htm"); 
?> 


(非常全面的一个php技术网站, 有相当丰富的文章和源代码.)
樣版的寫法如下:

templates/test3.htm:


<html>
<head>
<title>巢狀迴圈測試</title>
</head>
<body>
<table width="200" border="0" align="center" cellpadding="3" cellspacing="0">
    <{section name=sec1 loop=$forum}>
    <tr>
        <td colspan="2"><{$forum[sec1].category_name}></td>
    </tr>
    <{section name=sec2 loop=$forum[sec1].topic}>
    <tr>
        <td width="25"> </td>
        <td width="164"><{$forum[sec1].topic[sec2].topic_name}></td>
    </tr>
    <{/section}>
    <{/section}>
</table>
</body>
</html>
執行的結果就像筆者舉的例子一樣。

因此呢,在程式中我們只要想辦法把所要重覆值一層一層的塞到陣列中,再利用 <{第一層陣列[迴圈1].第二層陣列[迴圈2].第三層陣列[迴圈3]. ... .陣列索引}> 這樣的方式來顯示每一個巢狀迴圈中的值。至於用什麼方法呢?下一節使用資料庫時我們再提。

4.3 轉換資料庫中的資料

上面提到如何顯示巢狀迴圈,而實際上應用時我們的資料可能是從資料庫中抓取出來的,所以我們就得想辦法把資料庫的資料變成上述的多重陣列的形式。這裡筆者用一個 DB 類別來抓取資料庫中的資料,您可以自行用您喜歡的方法。

我們只修改 PHP 程式,樣版還是上面那個 (這就是樣版引擎的好處~),其中 $db 這個物件假設已經在 main.php 中建立好了,而且抓出來的資料就是上面的例子。

test3.php:
<?php
    
require "main.php"
    
// 先建立第一層陣列 
    
$category = array(); 
    
$db->setSQL($SQL1'CATEGORY'); 
    if (!
$db->query('CATEGORY')) die($db->error()); 
    
// 抓取第一層迴圈的資料 
    
while ($item_category $db->fetchAssoc('CATEGORY')) 
    { 
        
// 建立第二層陣列 
        
$topic = array(); 
        
$db->setSQL(sprintf($SQL2$item_category['category_id']), 'TOPIC'); 
        if (!
$db->query('TOPIC')) die($db->error()); 
        
// 抓取第二層迴圈的資料 
  &nb