原文地址:
https://docs.acquia.com/articles/drupal-8-advanced-render-array-patterns
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 模板中使用这些值:
<div {{ attributes }}> {{ element }} </div>
在预处理函数作用于 attributes 变量之后,他们被转变为对象,以便方便在 Twig 中修改和输出。这个处理由 ThemeManager.php 中的 render() 完成。
包装器(#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 冗余。因为系统使用的一些怪癖,有些核心同时使用了这两种方法。这些怪癖可能增添几分优雅,但带来的代价是增加了复杂度,把逻辑分离在多个地方。