9.4 高级渲染数组模式
HTML 属性
你能通过渲染数组设置 HTML 属性(例如 ID 和 Class)。这些属性会被传递到 Twig 模板,也允许 themer 添加和覆盖这些值。为了做这个,向渲染数组添加 #attributes 变量。数组的键是属性,值是属性的值。
$element = [
'#type' => 'my_element',
'#config_one' => 'blue',
];
$element['#attributes'] = [
'id' => 'my_blue_element_1',
'class' => ['my-element','blue'],
];
这允许辅助内容区中的预处理钩子对其进行覆盖 —– $variables[‘attributes’] 被 theme.inc 内的 template_process() 函数创建,template_process() 运行于所有的渲染数组。$variables[‘attributes’] 是一个副本,原始值存储在 $variables[‘element’][‘#attributes’] 内,但这个原始值基本不怎么用。
function hook_preprocess_my_element(&$variables) {
$variables['attributes']['id'] = 'my_blue_element_altered_id';
}
之后你可以在 Twig 模板中使用这些值:(见辅助内容区)
在预处理函数作用于 attributes
变量之后,他们被转变为对象,以便方便在 Twig 中修改和输出。这个处理由 ThemeManager.php
中的 render() 完成。
<div {{ attributes }}>
{{ element }}
</div>
包装器(#theme_wrapper)和容器
你能用 div 包装你的元素和使用之前的模式向这个包装器追加 class。通过向渲染数组添加 #theme_wrappers 实现这个。最常见的包装器类型是输出属性的一个容器。
$element = [
'#type' => 'my_element',
'#config_one' => 'blue',
'#theme_wrappers' => [
'container' => [
'#attributes' => [
'class' => ['my-wrapper-class']
],
],
],
];
对应的容器模板文件象这样:
container.html.twig
{%
set classes = [
has_parent ? 'js-form-wrapper',
has_parent ? 'form-wrapper',
]
%}
<div{{ attributes.addClass(classes) }}>{{ children }}</div>
探讨元素渲染数组流程
Drupal 7 支持分支系统的方式渲染元素。Drupal 8 希望简化这个,但很多旧的系统仍然残留。本课,我们跟随一个路径,这是核心内使用、推荐的一种模式。Drupal 核心仍然混杂使用所有的路径。
Element Plugin / pre_render / Twig pattern (见附图)
选择此模式背后的原因是保持与 Drupal 8 的一致,同时又不失灵活性。坚持面向对象模式,我们将所有元素定义为插件。“把所有的元素信息包含在一个插件文件内”符合 Drupal 8 对插件和类的结构化形式。所有元素在它们到达模板之前允许其他模块使用 hook_preprocess_hook 对其覆盖。因此我们保持了使用覆盖的能力。
与核心的差异
这种模式与核心之间的主要区别是多数核心也使用了 template_preprocess_hook
,这似乎与 #pre_render
冗余。因为系统使用的一些怪癖,有些核心同时使用了这两种方法。这些怪癖可能增添几分优雅,但带来的代价是增加了复杂度,把逻辑分离在多个地方。