18. 页面标题
本篇主题讲解drupal8系统是如何计算页面标题的,标题很重要,尤其对于搜索引擎优化来说,标题权重很高。
页面有标题当然是针对请求格式为html而言,在整个执行流程中如果控制器直接返回响应对象,那么标题计算就在控制器中随意进行。
流程仅仅停留在Symfony的渲染管道中,如果控制器返回的是渲染数组,那么将派发视图事件,主内容视图订阅器MainContentViewSubscriber将判断请求格式,并启动对应的格式渲染器渲染输出。今天的话题就发生在请求格式为html的HtmlRenderer渲染器中,这个渲染器用到了标题解析器。
这就是本主题的内容,下面看看标题是怎么计算出来的:
首先如果控制器返回的渲染数组包含#title子元素,如:$main_content['#title'],那么将原封不动的使用其值,标题计算完成。
如果没有包含,则调用标题解析器进行计算,标题解析器服务id为“title_resolver”
类:Drupal\Core\Controller\TitleResolver 构造函数接收控制器解析器:controller_resolver及字符串翻译服务:string_translation
标题解析源代码如辅助内容区所示:
首先查找路由定义中是否设置有标题回调,设置格式和控制器完全一样,键名为_title_callback,对回调的执行和控制器完全一样。
通过标题回调得到的结果不会被翻译,需要翻译需要在回调中进行。
如果没有设置回调则查找_title子元素,如果设置了则进行翻译工作(翻译系统介绍请看后续主题)
检查是否设置翻译上下文_title_context 此上下文用于翻译过程的消除歧义,比如may 到底翻译成“可以”还是“五月”呢?一般为空。
准备翻译参数,将请求中的_raw_variables值放入参数数组,_raw_variables仅包含占位符变量的原始值,不包括路由额外提供的变量。
然后检查路由是否提供额外标题参数,如果有则合并入参数数组。
然后将标题、参数、上下文传递给翻译系统进行翻译,标题计算工作完成。
优先级总结:控制器指定 》标题回调 》路由标题指定。
关于翻译系统的参数解释见:
https://api.drupal.org/api/drupal/core%21lib%21Drupal%21Component%21Ren…
翻译系统上下文解释见:
https://api.drupal.org/api/drupal/core%21lib%21Drupal%21Core%21Language…
public function getTitle(Request $request, Route $route) {
$route_title = NULL;
// A dynamic title takes priority. Route::getDefault() returns NULL if the
// named default is not set. By testing the value directly, we also avoid
// trying to use empty values.
if ($callback = $route->getDefault('_title_callback')) {
$callable = $this->controllerResolver->getControllerFromDefinition($callback);
$arguments = $this->controllerResolver->getArguments($request, $callable);
$route_title = call_user_func_array($callable, $arguments);
}
elseif ($title = $route->getDefault('_title')) {
$options = array();
if ($context = $route->getDefault('_title_context')) {
$options['context'] = $context;
}
$args = array();
if (($raw_parameters = $request->attributes->get('_raw_variables'))) {
foreach ($raw_parameters->all() as $key => $value) {
$args['@' . $key] = $value; //@和%表示要进行转义的状态,见下文解释
$args['%' . $key] = $value;
}
}
if ($title_arguments = $route->getDefault('_title_arguments')) {
$args = array_merge($args, (array) $title_arguments);
}
// Fall back to a static string from the route.
$route_title = $this->t($title, $args, $options);
}
return $route_title;
}
辅助内容区给出一个完整冗余的标题路由定义:
yunke.content:
path: '/yunkehello'
defaults:
_controller: '\Drupal\yunke\Controller\HelloController::content'
_title_callback: '\Drupal\yunke\Controller\HelloController::getTitle'
_title: 'Hello @user ! time:@May'
_title_context:'Long month name'
_title_arguments:
@May: "May"
@user: "yunke"
requirements:
_permission: 'access content'