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';
}
?>


付费阅读