Drupal 8 模块开发 9.1 : 创建元素,主题化元素和管理前端库

蒲公英 提交于 周四, 08/17/2017 - 16:51
Drupal8模块开发

原文地址:
https://docs.acquia.com/articles/drupal-8-creating-elements-theming-elements-and-managing-frontend-libraries  
 

本课...

  • 组装 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 oneexample two 两个渲染数组。example one 是最基本类型的渲染数组。最简单形式的渲染数组包含一个键 #markup 以及标记输出的一个关联值。

$output = [
 '#markup' => '<div>Markup Example</div>',
];

example two 这样比较复杂一些的渲染数组包含一组以 # 为前缀的变量。   第二个渲染数组最终将成为我们的元素标记。被 #type my_element 定义,包含 labeldescription 变量。此时它不包含链接或随机数孩子元素。这些我们将在后面追加。

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