71. 主题引述
从本主题开始,云客源码分析系列将连续讲解drupal主题相关内容,主要读者定位为drupal开发人员,以及想深入理解主题系统的前端人员,讲述系统是如何处理主题的,尽量兼顾到不会php的主题开发人员,提供许多要点知识,使他们读后更加深入的理解主题,对一些概念和用法有精确的掌握,由于这样的定位,将少讲解主题开发的入门知识,这方面的中文文章资料有很多,官方也较系统的进行了介绍,你应该先行查阅,如果有疑点或需要更加深入那么本系列将是你需要的。
这里推荐你阅读晴空写的《听晴空讲主题》系列文章,该系列还配有视频教程,晴空本人是中国为数不多的最专业的drupal主题开发者之一,作为主题开发人员对系统理解之深让云客惊讶。
由于阅读本主题时你应该已经掌握了主题基本概念,所以本主题将进行离散的讲解比较重要的知识点,也对官方文档含糊不清的地方给予清晰解释和深入剖析,后续主题将全面介绍系统与主题相关的组件和逻辑。
drupal主题系统的能力:
drupal主题系统十分灵活,你可以全站使用一套主题,用响应式设计去兼容移动端和pc端,如果响应式无法满足要求,你可以在各端分别使用不同的主题,但主题系统的灵活性远不止如此,实际上在同一个站点中你可以根据任意条件使用不同的主题,系统后台设定的只是默认值而已,比如在pc端或移动端你可以为不同语言采用不同主题,甚至不同用户采用不同模板,条件是任意的,只需要简单实现一个主题协商即可。
yaml格式:
关于yaml文件的规范请见官网http://www.yaml.org/ 网上有许多教程,不再讲述,这里仅提供一些注意事项及建议:
在扩展的info文件中,只有'type', 'core', 'name'是必须的,如果指定了'version'且全等于'VERSION',那么将采用drupal的版本号作为扩展的版本号(\Drupal::VERSION)
drupal中如果php的'yaml'扩展存在那么将使用YamlPecl::class来解码yaml文件,如果不存在那么回退使用YamlSymfony::class来解码,前者有很好的性能,但为了数据在不同环境下的一致性,在编码数据时一律使用后者。
如果你对yaml理解不深或者方便起见,可以直接使用系统提供的工具来产生yml文件以避免错误,这里是一个演示(请在控制器中执行):
$data = [
"str" => "字符串",
"str_with_space" => "yunke 云客",
"str_with_quotation" => "'' \"\"",
"str_with_slash" => "\\/",
"str_with_brace" => "{}[]",
"str_with_colon" => ":",
"str_null" => "",
"str_newline" => "yunke\nyunke",
"int" => 123,
"int_str" => "123",
"float" => 3.14,
"bool_true" => true,
"bool_str" => "true",
"null" => NULL,
"arr" => ["one", "two", "three"],
"arr_key" => ["a" => "one", "b" => "two", "c" => "three"],
];
$data = \Drupal\Core\Serialization\Yaml::encode($data);
file_put_contents("yunkeyaml.yml", $data);
$data = \Drupal\Core\Serialization\Yaml::decode($data);
var_dump($data);
如果需要读取一个yml文件,可以使用以下服务:
$data=\Drupal::service("info_parser")->parse($filename);
主题相关名字:
首先需要明白模块或主题都是drupal的扩展,有时我们使用名词“扩展”,你需要知道这既可能是指模块也可能是指主题,在部分语境下会特别指出是“模块扩展”或“主题扩展”,或者你需要根据叙述上下文来判断是指代什么,以下是一些和主题相关的名字及其含义:
机器名:
又叫主题扩展名,全局唯一,是识别主题的唯一标识,来源于info文件的文件名,如“bartik.info.yml”那么机器名就是“bartik”,不能包含空格,正则为:/^[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*$/,有些特定名字虽然满足该正则,但不能被使用,如“template”;下文中除非特别说明,当提到主题名时均指机器名。
人类可读名:
在info文件中name项指定的名称,这可以和机器名相同也可不同,显示在UI中,能包括空格
目录名:
存放主题相关文件的目录名称,注意:主题的扫描查找实际上是在查找内容满足要求(内容中必须有type属性)的以“.info.yml”后缀结束的文件,而不是依据目录结构信息,因此主题的目录名可以和机器名不同,但最佳实践应该相同,目录深度与结构不限,目录名不能使用:'src','lib','vendor','assets','css','files','images','js','misc','templates','includes','fixtures','Drupal',
这些目录在被扫描时会被跳过(可以利用这种特性隐藏多余的主题,不会对系统造成任何影响)
主扩展文件名:
默认为“机器名.扩展类型”,如“bartik.theme”、“search.module”,主扩展文件不是必须的,用于存放模块或主题需要的函数或定义常量,位于扩展根目录,是一个php文件,但并不以php做文件名后缀,如果存在则被默认加载。引擎主题是一个特殊情况,她的主扩展文件命名为“引擎名.engine”,如“twig.engine”。
主题扩展类型:
一般来说主题扩展是来定义站点外观的,在info文件中type项的值为 theme,这是主题开发人员需要的扩展类型,但引擎主题是一个例外:
drupal8可以同时使用多个模板引擎,默认采用twig,如果你有需要也可以同时使用Smarty等,一个模板引擎对应一个引擎主题,默认的twig对应的引擎主题是\core\themes\engines\twig,在info文件中引擎主题的type值为theme_engine,为什么说她是引擎主题呢?因为这样的扩展被放置在主题目录中,为什么说是例外呢?因为它本质上是一个和模块类似的功能型组件,并不定义外观,如调用主题引擎、查找主题钩子定义等
主题的继承:
可以一层一层的继承下去,层数不限,但每个主题只有一个直接基主题(父主题),也就是“单基多层继承”,这和php类继承类似。
drupal使用多个模板引擎时,如果在主题继承链中,子主题和基主题指定了不同的主题引擎,那么将以根基主题的设置为准。
当声明了基主题后,就自动声明了基主题依赖,在info文件中不必手动指定
继承链中每一个主题都需要被安装启用
主题扩展放置位置:
可以有三个地方,优先级递增(高优先级覆盖低优先级):
themes
sites/all/themes
sites/your_site_name/themes
有继承情况时,子主题可以放置在父主题的目录中,但并不推荐这样做,在目前的实现(drupal8.5)中对这种情况的处理也存在bug
主题info文件的附加默认值:
主题info文件被系统读取时会附加默认值,采用数组“+”操作,由此可见在没有指定选项时她会是什么样子,默认值被定义在主题处理器中,如下:
$defaults = [
'engine' => 'twig',
'base theme' => 'stable',
'regions' => [
'sidebar_first' => 'Left sidebar',
'sidebar_second' => 'Right sidebar',
'content' => 'Content',
'header' => 'Header',
'primary_menu' => 'Primary menu',
'secondary_menu' => 'Secondary menu',
'footer' => 'Footer',
'highlighted' => 'Highlighted',
'help' => 'Help',
'page_top' => 'Page top',
'page_bottom' => 'Page bottom',
'breadcrumb' => 'Breadcrumb',
],
'description' => '',
'features' => [
'favicon',
'logo',
'node_user_picture',
'comment_user_picture',
'comment_user_verification',
],
'screenshot' => 'screenshot.png',
'php' => DRUPAL_MINIMUM_PHP,
'libraries' => [],
];
由于采用数组后附加操作,所以当不想要某默认值时,需要自定义该值,比如不想要基主题那么需要明确定义为FALSE,否则默认为'stable'主题。
默认值被附加后,会派发system_info修改钩:
$moduleHandler->alter('system_info', $theme->info, $theme, $type);
这里 $theme是\Drupal\Core\Extension\Extension对象,info是对象外附加的公共属性,内容是应用了默认值的info文件内容数组,$type值为'theme',模块可以通过hook_system_info_alter()去修改她,接收三个参数,第一个和第三个参数实际上是包含在Extension对象中的,由于php对象默认是按引用传递所以模块可以修改该对象(修改$info数组参数将无作用,她不是引用传递),由于派发钩子的原因,意味着先安装的模块可以修改后面正在安装主题的info文件
模板文件命名:
不能出现下划线“_”,下划线使用连字符“-”代替,相反主题钩子中只能使用下划线,连字符用下划线代替。
补充:
- 在info文件里面注意空格的问题,如“'base theme'”项写成了“'base theme'”将失去作用,那影响了数组键名,在建立info文件时最好复制修改,避免这些小问题。