Drupal 8 主题开发笔记

参考:Drupal 8 主题开发指南
今天终于会用一点d8的主题,本着“没有女朋友不会自己扮哦”的心态自己造轮子吧。
大概日常的使用这里面都包含了,不够的话链接里面也会有,算是阅读比较好了,下一篇写一下Twig的语法(好像也不是很难)。

下面习惯性的先看目录结构:

d8主题文件夹结构

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
|-fluffiness.breakpoints.yml
|-fluffiness.info.yml
|-fluffiness.libraries.yml
|-fluffiness.theme
|-config
| |-install
| | |-fluffiness.settings.yml
| |-schema
| | |-fluffiness.schema.yml
|-css
| |-style.css
|-js
| |-fluffiness.js
|-images
| |-buttons.png
|-logo.png
|-screenshot.png
|-templates
| |-maintenance-page.html.twig
| |-node.html.twig
  • *.info.yml

    • 一个主题必须包含一个 .info.yml 文件用于定义该主题。同时 .info.yml 还定义了一些元数据(meta data),样式表(style sheets),以及区块区域。这是主题中唯一的必须包含的文件。
  • *.libraries.yml

    • .libraries.yml 文件是用于定于JavaScript库的 那些可以由主题来加载的。
  • *.breakpoints.yml
    • 断点(Breakpoints)定义在响应式设计中需要针对不同设备去响应的位置。断点定义在*.breakpoints.yml 文件里
  • *.theme
    • .theme文件是一个包含了所有条件逻辑和要输出的(预)处理数据的PHP文件。
  • css
  • js
    • 主题特定的 JavaScript 文件放在“js”文件夹里。一个主题想要加载 JavaScript 文件必须先定义在 .libraries.yml文件里。
  • Screenshot
    • 如果 screenshot.png 文件在一个主题里被发现,那么它将被用在外观(Appearance)页面。通常你可以在 .info.yml file中定义缩略图。
  • Logo
    • 如果 logo.svg 文件在一个主题里被发现,那么它将被用在网站的头部。Logo也可以通过“外观” > “设置”( Appearance > Settings)里上传。
  • Templates

其中重点就是.info.yml文件,从零开始造轮子就是靠它。

用 .info.yml 定义一个 Drupal 8 主题

Example

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
name: Fluffiness
type: theme
description: 'A cuddly theme that offers extra fluffiness.'
package: Custom
core: 8.x
libraries:
- fluffiness/globalstyling
stylesheets-remove:
- '@classy/css/layout.css'
- core/assets/vendor/normalize-css/normalize.css
regions:
header: Header
content: Content
sidebar_first: 'Sidebar first'
footer: Footer

Key

  • name: Fluffiness
    • 必填字段, 这个名字将显示在你需要激活主题的界面。
  • description: ‘An extra cuddly Drupal theme available in grey and blue.’
    • 必填字段, 描述和上面的名字相同,都是要显示在主题选择界面的。
  • package: Custom
    • 这个 Package 将允许你在管理主题界面归类。
  • type: theme
    • 必填字段,类型,告诉 Drupal 你这是什么类型的扩展文件。比如,模块,模板。对于主题来说,这里永远设置成 “theme”。
  • base theme: classy
  • core: 8.x
    • 必填字段,具体说明你的主题和 Drupal 核心兼容的版本。
  • version: 8.x-1.0
    • 对于 drupal.org 网站上的模块,版本号会被脚本自动填写。不建议手动去填写,可忽略。
  • screenshot: fluffiness.png
    • 在你的主题管理界面显示截图,如果您未使用此项,drupal 会自动查找并显示文件夹下的 screenshot.png。
  • libraries:

    - fluffiness/globalstyling

    • 此库文件可以包含 css 和 js 到所有的页面,详情,请阅读 https://www.drupal.org/node/2216195

      如果库文件被定义为:

      1
      2
      3
      4
      5
      6
      globalstyling:
      version: 1.x
      css:
      theme:
      css/style.css: {}
      css/print.css: { media: print }

      在 html 中将得到如下结果:

      1
      2
      <link rel="stylesheet" href="css/style.css" media="all" />
      <link rel="stylesheet" href="css/print.css" media="print" />
  • stylesheets-remove:

    - core/assets/vendor/normalize-css/normalize.css # 1

    - ‘@bartik/css/style.css’ # 2

    1. stylesheets-remove 将移除由另一个模板或模块添加的样式表。 CSS 文件完整路径将会显示,上面的只是文件名,为了适应多个文件的名称相同。
    2. 在删除Drupal核心文件(如jQuery UI中的CSS文件)时,要用完整文件路径。当文件属于某个模块或主题的组成部分时,要使用标记。注意,使用标记时需要进行引用,因为@在YAML中是一个保留标志。
  • regions:

    - header: Header

    - content: Content # required!

    - sidebar_first: ‘Sidebar first’

    - footer: Footer

    header,content,sidebar_first,footer 区块隶属于 regions。
    content 是必须要存在的区域(region)

Twig 模版的运用

Drupal允许您覆写用于生成HTML标记的所有模板,这样您就能完全控制定制主题中输出的标记。从高级HTML到小的字段,每个页面元素都有模板。

覆写模板(Overriding templates)

通过向遵循一定命名约定的主题文件夹添加模板,您就可以覆写Drupal核心模板。

要覆写模板,您:

  1. 找到想覆写的模板;
  2. 把模板文件从其基本位置复制到您的主题文件夹;
  3. (可选地)根据命名约定重新名命模板,以便针对要使用模板的更具体区域;
  4. 按您的喜好修改模板。

一旦您把模板文件复制进您的主题并清除缓存,Drupal就会开始用您的模板文件实例而不是基础模板。对于页面的任何部分,您都可以用Twig调试工具来找到其使用的模板。

主题钩子建议(Theme hook suggestions)

有时您想对模板文件进行修改,但仅限于使用到的一些地方。常见的例子是仅对特定类型的结点修改其结点模板文件。Drupal的主题层允许您遵循一定的名命约定来针对模板文件的特殊使用情况。当表现文章结点时,Drupal会首先寻找node--article.html.twig模板文件,如果它存在就加以使用。如果它不存在,Drupal就退回去使用默认的node.html.twig模板。Drupal用以决定一个模板可能使用的名称的过程叫做主题钩子建议。

主题钩子建议允许您用一定的命名约定,在您的主题中针对模板文件实现想要的覆写。

从核心、模块、主题引擎到主题的所有层次都可以提出建议。您可以用钩子来添加或修改建议:

  • hook_theme_suggestions_HOOK(array $variables)
  • hook_theme_suggestions_alter(array &$suggestions, array $variables, $hook)
  • hook_theme_suggestions_HOOK_alter(array &$suggestions, array $variables)

重建缓存(Rebuild cache)

使用主题钩子建议时,有可能Drupal在使用自己的缓存而非新模板所建议的。如果您遇到此问题,就清除缓存。要清除缓存,选择清除Drupal缓存中描述的方法之一。

背景信息(Background information)

您可以把建议想像成命名暗示,告诉系统根据正确的环境进行挑选。模板建议通过可修改的主题建议钩子进行设定。这些钩子允许任何模块或主题提供替代主题函数或模板命称建议,并重排或移除hook_theme_suggestions_HOOK()或该钩子的早期调用所提供的建议。

Drupal如何根据路径来确定页面主题钩子建议

这里是基于theme_get_suggestions()函数的另一种解释:

一个页面可能的模板列表由Drupal通过theme_get_suggestions函数生成,该函数由sytem_theme_suggestions_page()函数调用。

该页面的Drupal路历首先分解为其组件。如上所提及,Drupal路径并非其任意的别名:一个页面只有唯一一条Drupal路径。例如,”http://www.example.com/node/1/edit“ 和 “http://www.example.com/mysitename?q=node/1/edit“ ,其Drupal路径为node/1/edit,而其组件为”node”、1和 “edit”。

接着,将前缀设定为”page”。然后,对每个组件,遵循以下逻辑:

如果组件为数字,则向建议列表添加前缀和”__%”。
无论组件是数字与否,都向建议列表添加前缀和”__%”。
如果组件不是数字,则向前缀附加”__”和组件。

注意,要最终把建议变成实际文件名,”__”会变为”–“,而”.html.twig”则附加在建议后。于是,对于node/1/edit,我们得到下列建议:

page.html.twig (这总是一条建议)
page--node.html.twig (前缀设定为page__node)
page--node--%.html.twig
page--node--1.html.twig (因为是数字,前缀不变)
page--node--edit.html.twig (前缀设定为 page__node__edit)
page--front.html.twig (但只有当node/1/edit是首页时)

当页面实际渲染时,查看最后的建议。如果它存在,就使用该建议。否则,查看前一条建议,如此等等。当然,如果不存在覆写建议,那么page.html.twig就是最终的建议。这也解释了为什么page--front.html.twig存在的话会为首页覆写任何其它建议:它始终是指定为首页的页面的最后建议。

与Drupal 7的区别(Differences with Drupal 7)

以前,要改变模板建议,您就在预处理函数中修改$variables[‘theme_hook_suggestion’]和 $variables[‘theme_hook_suggestions’]来引入建议。在Drupal 8中,现在模块和主题在自己的专用钩子中定义和修改主题建议。

在Twig模板里提取和检索变量

在一个Twig模板文件里,你可以发现注释里有很多可用的变量。然而,当变量不是记录在模板文件中,或者是当主题、模块引入新的变量时,我们就需要一种方法来检索和提取模板里所有可用变量的和特定范围的变量,Twig支持在模板文件里使用dump() 函数(转存函数)来提取和检索变量。

Dump函数在启用调试之前不会显示输出的变量内容。学习如何启用Twig调试

开启调试后,dump函数就可以输出模板里的一个变量或多个变量的相关信息。
检索单个变量

假设你的模板里有一个title变量,可以按如下形式,将其内容dump到你的模板:

\{\{ dump(title) \}\}

检索模板内所有可用变量

将模板内所有可用的变量和变量的内容添加到你的模板,可用如下示例代码:

\{\{ dump() \}\}

Dump(转存)一个只有一个可用变量keys使用如下代码:

\{\{ dump(_context|keys) \}\}

以下全局变量可用于所有的Twig模板:

_self引用了当前模板以及模板包含的高级信息,也就是已编译的模板类名和Twig的环境信息。
_context 引用当前的上下文和上下文所包含的所有变量,然后传递给模板,例如一个来自theme()的变量,由预处理得来,或来自模板。增加一个\{\{ dump() \}\} 却没有定义一个变量,相当于添加了一个\{\{ dump(_context) \}\}。
_charset 用于引用当前的字符集。

使用dump()需注意:

如果你想看到模板里所有的变量,但是因dump()(转存函数)的递归或其他类似递归的算法逻辑而导致内存不足,你可以遍历_context来引用当前上下文所包含的所有变量,从而看到所有的keys,示例如下:

1
2
3
4
5
<ol>
\{% for key, value in _context % \}
<li>\{\{ key \}\}</li>
\{% endfor %\}
</ol>

然后用一个条件判断来检查结果(例如 \{\{ % if loop.index=2 %\}\}),然后dump(转存)有需要的变量即可。

最后是这次鼓捣的主要目的。

给主题添加区域

给主题添加区域(Region)需要满足以下两点:

  • 给你的THEMENAME.info.yml文件添加区域标记。

  • 编辑你的page.html.twig文件并输出新的区域。

在info文件里添加区域

首先需要在你的THEMENAME.info.yml文件里声明新的区域。区域的声明方式如下(声明在“regions:”下方):

1
2
3
4
5
# Regions
regions:
header: 'Header'
content: 'Content'
footer: 'Footer'

区域的键名必须是字母或数字,可以包含下划线(_)。键名必须以英文字母开头。在你的THEMENAME.info.yml文件中引用区域时,键名会被作为区域机器名,当然在代码里引用时也是如此。键值则作为人类可读的名字会被使用在区域的管理界面上。

在模板文件里添加区域:

为了把区域里的内容显示出来,你首先要确保你的区域已经添加到了page.html.twig文件中。

区域需要用Twig变量代替,以字符串page拼接区域的键名。

例如:

header: 'Header'

在你的Twig模板里会变成:

\{\{ page.header \}\}

这之后,你就可以把区域当作任何Twig变量一样,把内容输出到任何你需要输出的地方,以及用任何有意义的结构包裹。

模板文件的位置

如果你想知道哪个模板生成了指定元素, 你可以使用 Twig 的 debug 功能.
主题名字旁边也显示了文件的位置.

Twing debugging 可以在 sites/default/services.yml 文件内开启, 将twig.config 下的 debug 变量改为 true.
设置后,请清除缓存.

如果你查看当前页面下的源文件, 你将看到 debug 的输出类似下面 Twing templates 被渲染.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
<!-- THEME DEBUG -->
<!-- CALL: theme('block') -->
<!-- FILE NAME SUGGESTIONS:
* block--system.html.twig
* block--system-menu-block.html.twig
* block--system-menu-block--tools.html.twig
* block--bartik-tools.html.twig
x block.html.twig
-->
<!-- BEGIN OUTPUT from 'core/modules/block/templates/block.html.twig' -->
<div class="block block-system contextual-region block-menu" id="block-bartik-tools" role="navigation">
<h2>Tools</h2>
<div data-contextual-id="block:block=bartik_tools:|menu:menu=tools:"></div>
<div class="content">
<ul class="menu clearfix"><li class="first collapsed"><a href="/node/add">Add content</a></li>
<li class="last leaf"><a href="/block/add" title="Add custom block">Add custom block</a></li>
</ul>
</div>
</div>
<!-- END OUTPUT from 'core/modules/block/templates/block.html.twig' -->

下面是比较有用的

1
2
3
<!-- BEGIN OUTPUT from 'core/modules/block/templates/block.html.twig' -->
<!-- END OUTPUT from 'core/modules/block/templates/block.html.twig' -->

所有核心模板都在模块 /core/modules/system 文件夹下, Drupal 8 有一个子目录,名字为 “templates”

在这个文件夹内,我们可以找到所有类型的 模板文件 和 page.html.twig

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
system
...
|-templates
| |-admin-block-content.html.twig
| |-admin-block.html.twig
| |-admin-page.html.twig
| |-block--system-branding-block.html.twig
| |-block--system-menu-block.html.twig
| |-breadcrumb.html.twig
...
| |-menu-local-action.html.twig
| |-menu-local-task.html.twig
| |-menu-local-tasks.html.twig
| |-menu.html.twig
| |-page.html.twig
| |-pager.html.twig
| |-progress-bar.html.twig
...