模板語言和主題引擎
用Drupal的行話來說,主題就是一組負責你站點外觀的文件。你可以從http://drupal.org/project/Themes下載第3方主題,或者你可以自己動手創建一個主題,後者正是你在本章將要學習的。作爲一個web設計者,主題由你所熟悉的大部分內容所組成:樣式表,圖片,JavaScript文件,等等。你將發現,在Drupal主題和純HTML站點之間的區別就是模板文件。這些文件一般都包含大段的靜態HTML,和一些小段的用來插入動態內容的代碼。它們負責你站點的一個特定部分的外觀。模板文件的語法依賴於它所使用的主題引擎。例如,列表8-1,8-2,8-3列出了3個模板文件的代碼片段,它們輸出的內容是一樣但是包含的模板文件內容卻完全不同。
構建一個PHPTemplate主題
創建一個主題,可以有多種方式,這取決於你的起始材料。假定你的設計者已經爲你的站點提供了HTML和CSS文件。那麼將設計者的設計轉化爲一個Drupal主題,到底難不難呢?它實際上不是很難,而且你能夠輕易的完成工作的80%。不過還有20%—最後的難點了—它是 Drupal主題製作高手與新手的分水嶺。首先讓我們從簡單的部分開始。這裏有個概括:
- 爲站點創建或修改HTML文件
- 爲站點創建或修改CSS文件
- 創建一個.info文件,來向Drupal描述你的新主題。
- 按照Drupal的標準爲文件命名。
- 在你的模板中,插入可用的變量。
- 爲單獨的節點類型,區塊,等等創建模板文件。
每個theme至少包含一個描述文件.info,其它如果沒有則使用系統默認文件。主要包括模板文件,CSS文件等。
理解Drupal自帶模板文件
template.php
每個theme都可以包含一個template.php文件,用來包含邏輯代碼,這個文件Drupal會自動載入的,我認爲這個文件相對於Drupal模塊的.module文件一樣。其實一個theme和一個module非常相似,例如.info文件的寫法以及template文件和.module文件的寫法非常相似。
所以我可以在template.php文件裏,我們可以實現hook_theme等,此時你就認爲theme是一個module好了。
theme機制介紹
當Drupal想要爲一個可主題化的項目(比如節點,區塊,麪包屑,評論,或者用戶簽名)生成一些HTML輸出時,它將查找用來爲該項目生成HTML的主題函數或者模板文件。Drupal的所有部分,基本上都是可主題化的,這意味着,對於爲該項目實際生成的HTML,你可以進行覆寫。
Drupal的主題系統背後的核心哲理和鉤子系統的類似。通過遵循命名規範,就可以標識出哪些函數是主題相關的函數,它們負責格式化並返回你站點的內容,或者使用模板文件負責輸出HTML內容。
因此我們要輸出內容時一定要用theme機制實現,方便維護擴展,實現一個theme輸出主要有以下幾個步驟:
- 實現hook_theme(),目的是註冊我們的theme,會被寫入theme註冊表,當調用theme(‘themename’)時會去theme註冊表查找該theme。
- 使用函數或模板輸出內容。函數以theme_開頭,例如theme_breakcrumb()。如果是模板文件則以.tpl.php結尾。
- 實現hook_preprocess_HOOK來爲我們的輸出theme輸出準備變量,這步不是必須的。
- 其它開發人員如果要覆蓋你的輸出,則在他們的theme中按照一定的規則來覆蓋。
下面將詳細介紹整個過程,我們假定創建了helloTheme的theme,以及helloModule的module,用於展示我們的代碼。
theme_hook介紹
theme_hook通常在module中實現,事實上我們也可以在theme實現theme_hook,例如helloTheme_theme()。因爲我們之前說過一個theme某種程度上來說就是一個module。
function helloModule_theme() { return array( //theme_function的寫法 'hello' => array( 'variables' => array('abc' => NULL), ), //模板文件的寫法 'test' => array( 'variables' => array('abc' => NULL), 'template' => 'test', ), ); }
theme_hook在Drupal6和Drupal7中寫法是有點區別的,例如Drupal7中使用variables來傳遞變量,每個theme函數都會有個$variable參數。而Drupal6是用arguments。另外Drupal7中還引入了render element的寫法,具體見hook_theme
Drupal自帶的每個module基本上都實現了hook_theme,Drupal的核心theme的定義是在System模塊裏的system_theme中實現的,裏面調用了drupal_common_theme()函數來定義Drupal的核心theme,比如breadcrumb, table等。
theme函數輸出
函數以theme_開頭,加上我們註冊的theme名稱,例如我們上面註冊了hello的theme,則該theme函數的函數名爲theme_hello()。我們通過theme(‘hello’, array(‘abc’ => ‘Hello’))來調用我們的theme函數,abc這個參數可以通過$variable來獲取。Drupal會爲每個theme函數傳遞一個數組進來的,裏面包含了所有的變量。
function theme_hello($variables) { return "You say " . $variable['abc']; }
是選擇theme函數還是theme模板輸出,取決於你側重點。theme函數性能較好,執行較快,但是對於前端開發人員不友好,需要去理解PHP相關知識,而theme模板輸出對前端開發人員較友好,但是性能很差。
Drupal核心的theme函數都在includes/theme.inc中定義,比如theme_breadcrumb(), theme_table等。
theme模板輸出
根據theme_hook裏定義的模板文件名,我們需要在我們的module目錄下面創建一個test.tpl.php,在模板文件裏,我們可以直接通過$variables來獲取我們傳進去的參數,如$variables['abc'],同時Drupal會把參數直接轉換成變量,因此我們也可以直接訪問$abc。
<h2>You say <?php print $abc; ?></h2>
Drupal中核心的模板文件都在各自的module目錄下面,比如html.tpl.php, page.tpl.php都在system模塊下面,而node.tpl.php在node目錄下面。
爲輸出準備變量hook_preprocess_HOOK
通常我們在輸出時用到的數據都是調用時直接作爲參數傳進去,例如theme(‘hello’, array(‘abc’ => ‘Hello’)),但是以下情形時我們可以調用預處理函數來準備變量:
- 我們需要非常多的小的變量,如果通過參數傳遞會太長,因此可以在預處理函數裏進行預處理,將一個大的參數拆分成許多小的變量,典型的例子是node模塊爲node.tpl.php準備變量,參考node.module中的template_preprocess_node()函數。
- 我們擴展別人的theme輸出,或者不確定我們的參數時,我們可以在預處理函數來處理這些變量。
hook_preprocess_HOOK中的hook是指module名稱或theme名稱,HOOK是指我們定義的theme的名稱,比如helloModule_preprocess_hello(),預處理函數會被自動調用,我們只需要按照命名規則書寫就可以了。
我們以page.tpl.php爲例來看看預處理函數是怎麼工作的。在includes/theme.inc中有個template_preprocess_page用來給page.tpl.php準備各種變量,如果對page.tpl.php熟悉的話,應該知道page.tpl.php中有好多變量是可以直接使用的,比如$logo, $tabs, $language等,都是在這裏預定義的,在theme函數裏,可以通過$variables['logo']來訪問,而在模板文件中,Drupal會把這些變量轉換成直接變量,比如logo,我們既可以通過$variables['logo']來訪問,也可以通過$logo來訪問。
theme輸出的覆蓋
使用theme機制輸出的最大好處就是我們可以用自己的theme輸出來覆蓋別人的theme輸出,從而達到擴展的目的。以上面定義的hello爲例,會按照以下的優先級來執行:
helloTheme_hello() sites/all/themes/helloTheme/hello.tpl.php theme_hello()
phptemplate_hello()這種寫法在Drupal7中已經被取消了,就是在Drupal7的theme中,所有的前綴必須是themeName + “_”,例如galand_, helloTheme_等。
覆寫theme的常見做法就是在自己的theme目錄下建tpl模板文件或在temmplate.php中創建theme函數。對於tpl模板文件,同樣的文件在theme目錄下優先級是更高的,比如我們覆蓋node.tpl.php。
hook_preprocess_HOOK的覆寫
我們前面說過hook_preprocess_HOOK是用來給theme函數或模板準備變量的,但是它也是有好多寫法,有不同的執行順序的,
template_preprocess() template_preprocess_hello() helloModule_preprocess() helloModule_preprocess_hello() phptemplate_preprocess() phptemplate_preprocess_hello() helloTheme_preprocess() helloTheme_preprocess_hello() template_process()
上面提到的所有函數是從一個到最後一個順序執行的,就是說,如果上面的函數都存在,就會都執行一遍,如果他們提供了同名的變量,則後面的覆蓋前面的。這個和theme輸出是不一樣的,theme輸出只會執行優先級最高的那個。
這裏還是以template_preprocess_page爲例,這裏會先執行template_preprocess_page(),然後在執行template_process(),如果我們在theme裏定義了helloTheme_preprocess_page(),則會在template_process()之前執行。
Drupal中模板文件命名規則
上面說過,theme下面的同名模板文件比module下面的優先級高,比如helloTheme/node.tpl.php就比node/node.tpl.php優先級高,但是模板文件還可以根據命名的規則來確定優先級:
1、block–[region[module--delta]].tpl.php
基於主題文件: block.tpl.php
block–module–delta.tpl.php
block–module.tpl.php
block–region.tpl.php
2、comment–[node-type].tpl.php
基於主題文件: comment.tpl.php
3、comment-wrapper–[node-type].tpl.php
基於主題文件: comment-wrapper.tpl.php
4、forums–[[containertopic]–forumID].tpl.php
基於主題文件: forums.tpl.php
For forum containers:
forums–containers–forumID.tpl.php
forums–forumID.tpl.php
forums–containers.tpl.php
For forum topics:
forums–topics–forumID.tpl.php
forums–forumID.tpl.php
forums–topics.tpl.php
5、maintenance-page–[offline].tpl.php
基於主題文件: maintenance-page.tpl.php
6、node–[typenodeid].tpl.php
基於主題文件: node.tpl.php
node–nodeid.tpl.php
node–type.tpl.php
node.tpl.php
7、page–[frontinternal/path].tpl.php
基於主題文件: page.tpl.php
page–node–edit.tpl.php
page–node–1.tpl.php
page–node.tpl.php
page.tpl.php
page.tpl.php (this is always a suggestion)
page–node.tpl.php (and prefix is set to page__node)
page–node–%.tpl.php
page–node–1.tpl.php (prefix is not changed because the component is a number)
page–node–edit.tpl.php (and prefix is set to page__node__edit)
page–front.tpl.php (but only if node/1/edit is the front page)
8、poll-results–[block].tpl.php
基於主題文件: poll-results.tpl.php
9、poll-vote–[block].tpl.php
基於主題文件: poll-vote.tpl.php
10、poll-bar–[block].tpl.php
基於主題文件: poll-bar.tpl.php
11、profile-wrapper–[field].tpl.php
基於主題文件: profile-wrapper.tpl.php
12、search-results–[searchType].tpl.php
基於主題文件: search-results.tpl.php
13、search-result–[searchType].tpl.php
基於主題文件: search-result.tpl.php
Theme的調試輸出
- render(), hide()函數,這兩個函數是Drupal7新加的,便於可使節點的打印輸出更精細,render()用來構造內容,比如print render($page['highlighted']);,因爲Drupal對構造過的內容會做標識,下次不會再次構造,因此hide()就是把標識去掉,可以接着render了。
- Devel是個強大的Drupal開發工具,主題開發者模塊是依賴於Devel的一個模塊,能夠讓你指定一個頁面元素,來查看生成該元素所用到的模板文件和主題函數,以及對於該元素都有哪些變量(和它們的值)可用。