D7 theme API 概述
对于Drupal的主体来说,如果是比较简单的项目,直接在主题模板tpl的基础上覆写或是配合主题的预处理器基本上就可以实现需求。而对于高度定制化的需求,Drupal也可以很方便的实现需求,这里就不得不说到drupal在主题层面上提供了强大的API供大家使用。
主题渲染的流程
$flow
st=>start: 路由匹配回调执行 hook_menu callback
e=>end: 前端 html
op1=>operation: 可渲染数组 Renderable array
op2=>operation: 预处理加工 preprocessor
op3=>operation: 处理加工 processor
op4=>operation: 模板打印输出 tpl.php
st->op1->op2->op3->op4->e
$
页面html的生成与可渲染数组Renderable Array
对于D7来说,一个页面的整体完全是由一个大数组构建并按需将数组元素转化为html,构成页面的建筑材料以数组作为规范,也可以是一个AJAX响应,或者一个由hook_menu定义的路由回调的反馈结果,这也就意味着你的页面回调应该返回一个数组而不是字符串作为结果,数组的好处是结果可以很方便的增删改,而整串html拼接的字符操作起来灵活性很差。在这里我们统一将构成页面的数组称为可渲染数组(Renderable array).
一个D7页面响应回调的例子
//页面的两部分表格+分页,而实际的项目中,很可能还会有其他的元素,比如页头、页脚、侧边栏等加入构建完整页面所需要的数组中
//这个例子中,module_name_page函数很可能是一个menu路径的响应回调函数
//回调的结果$build就是一个可渲染数组
function module_name_page() {
$build = array();
$build['table'] = array(
//键名含有'#'的表示的是主题的属性值,不带'#'的一般为该主题元素的子元素
//这里需要注意的是#theme和#theme_wrappers这两个属性,这两个属性建对应的字符串值实际上是生成html的回调函数
//'#theme'的值'table'负责表格的生成
//'#theme_wrappers'在'#theme'执行完之后执行,可以在渲染后的html外添加自定义的html,例如在字段的html外添加字段组html的包裹元素如div等,注意'#theme_wrappers'的回调函数的执行结果必须包含元素的'#children'属性,该属性包含'#theme'回调函数生成的html和子元素
'#theme' => 'table',
'#header' => $header,
'#rows' => $rows,
);
$build['pager'] = array(
'#theme' => 'pager',
);
return $build;
}
注:关于 '#theme'和 '#theme_wrappers',官网API文档drupal_render( )有详细说明
前端对响应回调的处理
针对响应的可渲染数组,Drupal将这些结果交给主题的预处理器(preprocessor)和处理器(processor)进一步加工处理以方便在PHPTemplate的模板文件tpl中使用。
以一个node的页面为例
node页面的内容(renderable array)由函数node_view提供,回调结果提供给template_preprocess_node( )来预处理,预处理的作用是将大数组Renderable Array分解为一个个小数组,而这些小数组可以直接在模板文件node.tpl.php直接打印出来,打印完的结果,就是最终的html页面了。
function node_view($node, $view_mode = 'full', $langcode = NULL) {
if (!isset($langcode)) {
$langcode = $GLOBALS['language_content']->language;
}
// Populate $node->content with a render() array.
node_build_content($node, $view_mode, $langcode);
$build = $node->content;
// We don't need duplicate rendering info in node->content.
unset($node->content);
$build += array(
'#theme' => 'node',
'#node' => $node,
'#view_mode' => $view_mode,
'#language' => $langcode,
);
// Add contextual links for this node, except when the node is already being
// displayed on its own page. Modules may alter this behavior (for example,
// to restrict contextual links to certain view modes) by implementing
// hook_node_view_alter().
if (!empty($node->nid) && !($view_mode == 'full' && node_is_page($node))) {
$build['#contextual_links']['node'] = array('node', array($node->nid));
}
// Allow modules to modify the structured node.
$type = 'node';
drupal_alter(array('node_view', 'entity_view'), $build, $type);
return $build;
}
// 这里的view_mode、teaser、title、page等将可以在模板文件中被直接打印
function template_preprocess_node(&$variables) {
$variables['view_mode'] = $variables['elements']['#view_mode'];
// Provide a distinct $teaser boolean.
$variables['teaser'] = $variables['view_mode'] == 'teaser';
$variables['node'] = $variables['elements']['#node'];
$node = $variables['node'];
$variables['date'] = format_date($node->created);
$uri = entity_uri('node', $node);
$variables['node_url'] = url($uri['path'], $uri['options']);
$variables['title'] = check_plain($node->title);
$variables['page'] = $variables['view_mode'] == 'full' && node_is_page($node);
// Flatten the node object's member fields.
$variables = array_merge((array) $node, $variables);
// Helpful $content variable for templates.
$variables += array('content' => array());
foreach (element_children($variables['elements']) as $key) {
$variables['content'][$key] = $variables['elements'][$key];
}
...
}
node.tpl.php模板文件
<div id="node-<?php print $node->nid; ?>" class="<?php print $classes; ?> clearfix"<?php print $attributes; ?>>
<?php print $user_picture; ?>
<?php print render($title_prefix); ?>
<?php if (!$page): ?>
<h2<?php print $title_attributes; ?>><a href="<?php print $node_url; ?>"><?php print $title; ?></a></h2>
<?php endif; ?>
<?php print render($title_suffix); ?>
<?php if ($display_submitted): ?>
<div class="submitted">
<?php print $submitted; ?>
</div>
<?php endif; ?>
<div class="content"<?php print $content_attributes; ?>>
<?php
// We hide the comments and links now so that we can render them later.
hide($content['comments']);
hide($content['links']);
print render($content);
?>
</div>
<?php print render($content['links']); ?>
<?php print render($content['comments']); ?>
</div>