Drupal 7 Render elements

Render elements are new to Drupal 7's theme layer. They've existed since Drupal 4.7 as part of the Form API, but they've now been injected into the heart of the theme system. A  Render element is a complex data structure passed as a single parameter to theme(), as one of its variables. Render elements are fundamentally nested arrays 
that can include:
  • The data to be rendered
  • Other render elements which are considered "children" of the element
  • An array of structures such as CSS and JavaScript files, that should be attached to the page when it is rendered
  • A list of theme hooks that can be used to theme the data
  • A list of callback functions to run on the element before and after it   is themed

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:

  1. A render element  property . These are prefixed by #.
  2. 所有没有#前缀的都表示子元素A child element
  3. 传递到theme函数的变量,渲染元素的过程中会查看变量的前缀,但是theme()函数在找到合适的主题接口之后会去掉#

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 properties渲染属性

Render element的属性在两个地方有定义:一个是在drupal_render()中直接定义,以及他的帮助函数,下面的属性都是drupal_render()使用的:

  • #access: 布尔值,用来指定当前用户是否有权限查看元素。
  • #cache: 缓存项 An array indicating whether the element should optionally   be retrieved from cache or stored in cache after rendering. See  drupal_render() for more information.
  • #markup: HTML标记符包裹的字符串,如果已经设置了这个属性,#type属性就不需要设置了
  • #type: 字符串,指明哪个元素会被渲染,A string indicating which element is being rendered. The default properties for this type of element are extracted from the data specified   with hook_element_info() and merged with the render element.
  • #defaults_loaded: A Boolean indicating whether the element type's default properties have already been loaded. If this is false or not set, the default properties from element_info() are added before  drupal_render() looks at any other render properties (except for #access and #cache).
  • #pre_render: 数组,应用于元素被主题化之前回调函数
  • #theme: 字符串:指明哪个主题hook应用于此元素
  • #theme_wrappers: 数组:应用于主题hook之后调用的函数.
  • #post_render: An array of callbacks to apply to the element after theming.
  • #children: The rendered element and its children. It is normally built up internally by  drupal_render() as it renders the elements, but can also be   set by a  #pre_render callback.
  • #prefix: A string containing markup to be prepended to the   
  • #children property.
  • #suffix: A string containing markup to be appended to the   #children property.
  • #weight: A number used to sort child elements.
  • #sorted: A Boolean indicating if the child elements have already been sorted. Since sorting a render array is expensive, if you know the data is already sorted (for example, the data was sorted when retrieved from the database), you should set this property to  TRUE.
  • #states: JavaScript state information.
  • #attached: An array of CSS, JavaScript, libraries, or other associated attachments related to the element. See drupal_process_attached()  for more information.
  • #printed : 布尔值:指明元素是否准备渲染

hook_element_info

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



如你所见,#type为link的元素会被传递到两个#pre_render 函数,drupal_pre_render_link() 函数用于查找特殊的渲染元素的属性,在我们之前的例子中有 #title,  #href, and  #options 属性。
重申一遍,hook_element_info()为他的渲染元素类型定义了默认的属性,它也在自己的内部API指定特别的渲染回调 定义渲染元素的属性。
使用这个框架,模块可以自己创建复杂的渲染数组,调用drupal_render()会在hook_element_info()使用指定属性。



hook_page_alter()

什么是关键点呢? 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()两个非常重要的作用就是:


  1. hook_page_alter()允许插入block到page的主要内容里面
  2. 或者反过来,移动一个字段到当前的页面区域。


The power of theme()

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:


Drupal 7 Render elements_第1张图片
We've actually already discussed most of the work flow of theme(). There's only one aspect we haven't yet seen. So far, we've only called theme() with a simple string passed to its  $hook parameter. However, we can actually pass more complex data to it and make use of the theme system's  theme hook suggestions.


Theme hook suggestions

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);





你可能感兴趣的:(Drupal 7 Render elements)