Drupal 主题模板方案(Template Suggestions)与覆写机制的实现和原理简述
Drupal 在主题制作方面有一套灵活的开发机制,通过使用级联的模板方案(Template Suggestions),使开发人员能够灵活地扩展和使用 Drupal 模板(.tpl.php)文件。本文将结合相关源代码简单介绍此机制的实现方式与用法。
一个 Drupal 页面的显示可以简单地分为两步,第一步为模板脚本(template.php)准备数据,第二步将模板脚本准备好的数据填充到模板文件(.tpl.php)中。这样一来,模板文件主要用于定义网页的布局,模板脚本则包含生成模板文件中变量的逻辑代码(这样便将业务逻辑层和表示层分离,使得程序员和设计人员可以更好地分工和合作)。
以节点页面为例,程序员通过 template_preprocess_node() 函数将节点的 $title, $content, $node_url 等数据准备好,设计人员制作 node.tpl.php 模板,并将 $title, $content, $node_url 等变量放置到模板中即可(本文的最后一段源代码的前几行,便是 template_preprocess_node() 函数在为模板文件(.tpl.php)准备变量)。
在 Drupal 中,模板脚本也是级联的,关于模板脚本的加载和运行顺序请参考《使用 preprocess 函数处理模板文件中使用的变量》。
Drupal 使用 drupal_discover_template() 函数查找并加载模板文件(.tpl.php),通过逆向查询传入的 $suggestions 数组,来确定使用哪个模板对当前页面进行主题化。以下是 drupal_discover_template() 函数的源代码
<?php
function drupal_discover_template($paths, $suggestions, $extension = '.tpl.php') {
global $theme_engine;
// 移除斜杠和空字符,以免程序使用不可用的文件路径
$extension = str_replace(array("/", "\\", "\0"), '', $extension);
// 以 FIFO 顺序轮循 $suggestions 数组中的元素
// 说明:FIFO 是指 First In First Out,即先进入数组的元素,先被取出(先进先出)
$suggestions = array_reverse($suggestions);
$paths = array_reverse($paths);
foreach ($suggestions as $suggestion) {
if (!empty($suggestion)) {
$suggestion = str_replace(array("/", "\\", "\0"), '', $suggestion);
foreach ($paths as $path) {
// 程序将遇到且存在的第一个模板文件名,作为此函数的返回值
if (file_exists($file = $path . '/' . $suggestion . $extension)) {
return $file;
}
}
}
}
}
?>
这里需要注意,$suggestions 数组在进行轮循之前被进行了一次数组翻转(array_reverse)操作,这么做的目的在于将最后添加到模板列表中的模板最先被取出,即 FILO-First In Last Out,从而能够实现模板文件的级联与覆写。
因此,在加载模板之前,应该先定义 $suggestions 数组,这样才可以让 drupal_discover_template() 从所有模板文件中返回当前页面所使用的模板文件。所以,如果我们要自己添加模板,还需要知道如何设置 $suggestions 变量。
<?php
function theme() {
//... 略去前面部分代码
// 初始化 $suggestions 变量
$suggestions = array();
// 如果 $variables 变量中定义了 'template_files' 或者 'template_file',则将它们赋值或加入到 $suggestions 数组
if (isset($variables['template_files'])) {
$suggestions = $variables['template_files'];
}
if (isset($variables['template_file'])) {
$suggestions[] = $variables['template_file'];
}
// 将 $suggestions 数组作为参数传入 drupal_discover_template() 函数,返回 $template_file(模板文件)
if ($suggestions) {
$template_file = drupal_discover_template($info['theme paths'], $suggestions, $extension);
}
//... 略去后面部分代码
}
?>
通过以上代码可知,传入 drupal_discover_template() 函数的 $suggestions 数组,是来自于变量 $variables[‘template_file‘] 或者 $variables[‘template_files‘]。可以在 template.php 中的预处理函数中为 $variables 赋值。
以预处理函数 template_preprocess_node() 为例,在打开节点页面时,Drupal 会先查找 node-[node_type].tpl.php 作为此页面的模板。
<?php
function template_preprocess_node(&$variables) {
//... 略去前面部分代码
// 为模板设置变量
if ($variables['teaser'] && $node->teaser) {
$variables['content'] = $node->teaser;
}
elseif (isset($node->body)) {
$variables['content'] = $node->body;
}
else {
$variables['content'] = '';
}
//...
// 将 'node-' . $node-type 模板加入模板数组,如果内容类型为 page,之后将会尝试加载 node-page.tpl.php
$variables['template_files'][] = 'node-' . $node->type;
}
?>
因此,当我们要使用自定义模板时,只需要在模板脚本的预处理函数中,将模板名赋值到 $variables['template_file'] 或者 $variables['tempalte_files'][] 即可。示例:
<?php
function themeName_preprocess (&$variables) {
$variables['template_files'][] = 'template_name';
}
?>