In template files, render elements are handled slightly differently then normal variables, using the syntax we saw earlier in our typical-hook.tpl.php example:
<?php print render($element); ?>
In theme functions, render elements are included with its output using the drupal_render() function:
$output .= drupal_render($element);
来看一个简单的例子:
$element = array( '#prefix' => '<div class="plain">', '#markup' => '<p>' . t('There is no spoon.') . '</p>', '#suffix' => '</div>', );
In the preceding render element our main property is the #markup property which uses a string containing HTML markup as-is for the rendered element. The other properties do exactly what they hint at, prepending or appending HTML markup to the rendered element. If drupal_render($element) was called, it would simply return the three strings concatenated together.
Now, that was an extremely simple example, but when we start looking at more complex render elements, we'll see that each array key in a render element can be one of the following three things:
Taking these slightly mush rules, we can examine the following render element:
$element = array( '#prefix' => '<div class="less-simple">', '#suffix' => '!</div>', 'kitten' => array( '#type' => 'link', '#title' => t('Kill me'), '#href' => 'admin/core/hack', ), 'separator' => array( '#markup' => '<br />', ), 'domo' => array( '#theme' => 'username', '#account' => $account, ), );
首先,我们可以非常直观的看到我们的渲染元素的子元素是kitten, separator, and domo。separator 子元素是一个简单的 #markup 渲染元素。
再来看看domo元素,我们可以看到#theme的属性是设置成username,drupal_render()会将它传递到username主题函数(也就是theme_username()),意味着theme('username',$element['domo'])会被调用theme()会在所有的变量传递到theme_username之前去掉#符号。
最后,kitten元素的#type属性设置为link,#type属性是告诉drupal_render()如何渲染元素。在我们学习了hook_element_info()之后我们会明白的,现在我们可以这样理解,drupal_render()会传递kitten元素到drupal_pre_render_link()函数,它将渲染元素使用l()函数,并且返回字符串。
Render element的属性在两个地方有定义:一个是在drupal_render()中直接定义,以及他的帮助函数,下面的属性都是drupal_render()使用的:
The second place properties are defined is inside hook_element_info(). Each #type of render element needs to be defined in an implementation of hook_element_info(). system_element_info() defines most of Drupal core's render elements, which include several useful elements such as the markup element, the link element, and all the form elements. The following is a short snippet from system_element_info():
[ 80 ] /** * Implements hook_element_info(). */ function system_element_info() { // HTML markup. $types['markup'] = array( '#markup' => '', '#pre_render' => array('drupal_pre_render_markup'), ); // A HTML link. $types['link'] = array( '#pre_render' => array('drupal_pre_render_link', 'drupal_pre_render_markup'), ); // A hidden form element. $types['hidden'] = array( '#input' => TRUE, '#process' => array('ajax_process_form'), '#theme' => 'hidden', ); return $types; }
什么是关键点呢? By creating these complex render elements, we delay rendering of the data and allow opportunities to alter that data before it is rendered into a string. Before render elements were used in Drupal's theme system, themers and module developers often had to completely re-render data after it had been rendered the default way. This was obviously inefficient. Now each of these render elements can be altered in preprocess functions or even directly in a template file with the show() and hide() functions.
如果模板文件抵用hide($element['child']),其实就是简单的设置它的#printed属性为true,所有当我们print render($element)时,它会延迟调用,子元素不会打印,我们可以在之后 print render($element['child']),render()会设置#printed为false,传递元素到drupal_render()。
在使用hook_block_view()这个钩子函数都会返回渲染好的元素,任意的menu callback会使用一个页面的主要内容,也会返回渲染元素。
当页面的主要内容恢复,drupal_render_page()会装饰$page元素使用hook_page_build()。通过block模块的
block_page_build(),所有页面的区域会添加到$page元素作为子元素。每个区域的子元素就是一个个blocks了。druapl_render_page()允许模块通过hook_page_alter修改$page渲染数组
hook_page_alter()两个非常重要的作用就是:
It turns out that the theme() function has to do quite a bit of work once it's called. The following diagram should make its responsibilities and its order of operations clearer:
So re-using theme hooks in various places in our code is a good thing, of course. However, one problem you'll encounter is that theme functions lose context when a theme hook is re-used. For example, theme_links() has no idea if it's theming node links or comment links, which makes it difficult to style them differently. Fortunately, we can provide context to the theme system by providing a theme hook pattern as its first parameter:
[base hook]__[context]
The parts of the pattern are separated with a double underscore since some theme hooks (like user and user_profile) could be confusing if we were to use a single underscore to delineate the parts of the pattern. In fact, we can provide additional contexts if needed, as follows:
[base hook]__[context]__[even more context]__[don't get crazy]
So how does this work and how does it help? In Drupal 7, we theme the node's links by calling theme('links__node', $vars). So theme() will use a theme_links__node() function if one has been provided. However, if one doesn't exist, it will use theme_links() . This allows us to know the context based on the theme function we are implementing. A more complex example is when Drupal 7 themes the node's contextual links; it calls theme('links__contextual__node', $vars). So, theme() will search for a theme_links__contextual__node(), then for theme_links__contextual(), and lastly theme_links() , shortening the pattern by one unit of context each time.
The theme hook pattern is an easy-to-use method of providing context, but some contributed modules need to provide more complex lists of context than the simple string pattern can provide. For this reason, an array of possible hook suggestions can also be passed to theme(). For example, both Views and the Menu block module use this method. While theming trees of menus, Menu block provides the following array to theme:
$hook = array( 'menu_tree__menu_block__' . $delta, 'menu_tree__menu_block__' . $menu_name, 'menu_tree__menu_block', 'menu_tree__' . $menu_name, 'menu_tree', ); theme($hook, $tree);