本课...
- 组装 examples/theme_example 模块
- 定义一个元素
- 通过渲染数据流处理一个元素
- 学习 Twig 模板
- 学习向元素追加 JavaScript 和 Css
- 学习 Drupal 8 中使用的其他处理模式
本课将演示怎样创建页面的一个组件(叫做元素),包含了标记以及 JavaScript、Css 等前端这些东西。我们将展示如何在 PHP 后端定义这个元素,以及它被处理、随着 JavaScript 和 CSS 渲染到页面的整个过程。
定义
元素
Drupal 里,页面的片段被组织成元素,这些元素经常会包含其他的元素。因此我们把这些片段捆绑为大的嵌套结构,修改这些片段,之后渲染为页面或处理为其他的输出形式。元素包括链接(links),按纽(buttons),字段(fields),图片( images),节点(nodes),区块(blocks),页面(pages)等等。所有东西都是某种类型的元素。
渲染数组
PHP 概念中被用于存储元素信息的东西叫做渲染数组(render array),它包含可渲染元素的所有信息。
渲染数组包含定义元素片段信息的元素(例如它的类型)和变量。例如,一个链接有一个类型 link 和使用 title、URL 产生一个 HTML <a> 元素。链接也有很多其他可选的变量,例如 class 和 id 。
之前的课程里我们已经看过控制器产生渲染数组,这些渲染数组通常都是最基本的形式。渲染数组系统把元素呈现为很基本的形式,然后通过可扩展系统加工它们直至成为 HTML 标记。
在处理过程中,默认值会被添加到渲染数组,其他逻辑被模块和主题定义的元素类型相关代码执行,最后渲染数组会被交付给产生标记的模板。
我们将构建一个简单元素,看下从渲染数组到形成最终元素的处理流程。所有渲染数组会包含以下属性中的一个:
- #markup
- #type
- #theme
本课大多数时间里,我们将关注 #markup 和 #type 。
Twig 模板
模板是把渲染数组转变为标记的文件。通常模板会包含带有占位符的标记。
<aside> {{ element.aside }} </aside>
Twig 是 Drupal 8 中使用的一个基于 PHP 的模板引擎。Drupal 之前的版本,模板文件使用 PHP 本身转换渲染数组、创建 HTML 标记。之前的版本也偶尔使用 PHP 函数直接输出标记。Drupal 8 只推荐使用模板输出标记。
模板引擎改为 Twig 使模板创建更简单,更安全。
Drupal 之前的版本,因为安全和性能的原因,总被告知不要在模板文件内书写逻辑。Twig 限制比较多,它提供的结构有助于缓解这种担忧,语法相对于其他前端系统比较简单,前端开发者更容易上手。
JavaScript / 样式表/ 前端库
Drupal 8 里,我们捆绑与元素相关的所有 JavaScript 和 Css 为一个前端库,附加(使用 #attached)这个库到渲染数组。这也是 Drupal 7 中被推荐的方式,但很少被使用。Drupal 7 中也支持运行中追加 JavaScript 和 CSS。
除了一些小的变化,JavaScript 和 CSS 本身与在之前的 Drupal 没太大区别。
定义我们的元素
本课我们将创建一个叫做 My Element 的元素,它包含一个链接、一个描述和一个随机数。这个元素也会加载相关的 CSS 和 JavaScript。
<div> <div>Random Number: 42</div> <p>Here is a description</p> <a href="http://www.drupal.org">Drupal.org</a> </div>
要显示这个元素,我们将创建一个包含这个元素的页面。
安装我们的模块
我们快速创建模块,提供显示这个新元素的例子页面。 theme_example.info.yml
name: Theme Example type: module description: Example showing how to add a custom element and add JavaScript and CSS as a library core: 8.x package: Examples
theme_example.routing.yml
theme_example.simple: path: 'examples/theme-example/simple' defaults: _controller: '\Drupal\theme_example\Controller\ThemeExampleController::simple' requirements: _access: 'TRUE'
src/Controller/ThemeExampleController.php
<?php /** * @file * Contains \Drupal\theme_example\Controller\ThemeExampleController. */ namespace Drupal\theme_example\Controller; use Drupal\Core\Controller\ControllerBase; /** * Controller routines for theme example routes. */ class ThemeExampleController extends ControllerBase { public function simple() { return [ 'example one' => [ '#markup' => '<div>Markup Example</div>', ], 'example two' => [ '#type' => 'my_element', '#label' => $this->t('Example Label'), '#description' => $this->t('This is the description text.'), ], ]; } }
创建渲染数组
在这个控制器(ThemeExampleController.php)里,我们创建了三个元素渲染数组。第一个是容器,包含 example one 和 example two 两个渲染数组。example one 是最基本类型的渲染数组。最简单形式的渲染数组包含一个键 #markup 以及标记输出的一个关联值。
$output = [ '#markup' => '<div>Markup Example</div>', ];
像 example two 这样比较复杂一些的渲染数组包含一组以 # 为前缀的变量。 第二个渲染数组最终将成为我们的元素标记。被 #type my_element 定义,包含 label 和 description 变量。此时它不包含链接或随机数孩子元素。这些我们将在后面追加。
$output = [ '#type' => 'my_element', '#label' => $this->t('Example Label'), '#description' => $this->t('This is the description text.'), ];
用渲染元素插件定义一个元素
我们已经看到元素可以被包含在控制器或其他代码里,我们需要定义元素是什么,以及它如何被处理。像其他插件一样我们在 src/Element/ 里创建一个新类,继承基类 RenderElement。这种形式允许我们把元素相关的所有信息和逻辑存储在一个文件内。
注解
这个元素的注解定义元素名,这样系统才能找到它。我们追加注解 @RenderElement,并设置为我们的 #type 名。
getInfo()
getInfo() 函数定义了我们的元素是怎样的结构。我们提供了默认值,也用 #type 相同的名字追加了一个 #theme 变量。稍后我们将在模块的 hook_theme() 内定义这个 #theme 。大多数类似的配置已经移到 YML 文件,但主题系统在 Drupal 8 内还是有些复杂。
#pre_render 函数
我们的组件包含一个 #pre_render 函数,用于处理基本的渲染数组以及为模板创建必要的片断。Drupal 7 里,预处理函数做这些事情。预处理函数仍然存在,只是对模块和主题有意义,不再定义元素。在 pre_render 函数里,我们使用变量创建了一个渲染数组,包含几个孩子和一个变量 #random_number 。
src/Element/MyElement.php
<?php /** * @file * Contains \Drupal\theme_example\Element\MyElement. */ namespace Drupal\theme_example\Element; use Drupal\Core\Render\Element\RenderElement; use Drupal\Core\Url; /** * Provides an example element. * * @RenderElement("my_element") */ class MyElement extends RenderElement { /** * {@inheritdoc} */ public function getInfo() { $class = get_class($this); return [ '#theme' => 'my_element', '#label' => 'Default Label', '#description' => 'Default Description', '#pre_render' => [ [$class, 'preRenderMyElement'], ], ]; } /** * Prepare the render array for the template. */ public static function preRenderMyElement($element) { // Create a link render array using our #label. $element['link'] = [ '#type' => 'link', '#title' => $element['#label'], '#url' => Url::fromUri('http://www.drupal.org'), ]; // Create a description render array using #description. $element['description'] = [ '#markup' => $element['#description'] ]; $element['pre_render_addition'] = [ '#markup' => 'Additional text.' ]; // Create a variable. $element['#random_number'] = rand(0,100); return $element; }
hook_theme()
在 hook_theme() 里,重新定义了我们的元素,指定使用渲染元素模式,命名这个元素为”element“。这提供了一个 Twig 变量元素。使用最广泛的就是渲染元素模式和 Twig 模板。在之前的 Drupal 版本,大多数元素信息被定义在 hook_theme() 内,但在 Drupal 8 里,这些逻辑现在在插件内。
theme_example.module
<?php /** * Implements hook_theme(). */ function theme_example_theme() { $items = [ 'my_element' => [ 'render element' => 'element', ], ]; return $items; }