80. 占位符策略placeholder_strategy
在系统派发响应事件时,渲染数组中的占位符内容($element['#attached']['placeholders'])以及其他附属物还没有被渲染和替换,此操作发生在同样是订阅响应事件的html响应订阅器中(服务id:html_response.subscriber)具体在附属物处理器中替换(服务名:html_response.attachments_processor),在派发响应事件时,系统在html响应订阅器之前提供了一个订阅器:
服务名:html_response.placeholder_strategy_subscriber
执行方法:onRespond
优先级:5-1
这个订阅器执行占位策略服务,核心或模块可以提供占位策略服务来修改占位内容(见下);实际上系统中的所有占位策略服务都被链式占位策略服务收集并执行,见下。
链式占位策略服务:
服务名:placeholder_strategy
类:Drupal\Core\Render\Placeholder\ChainedPlaceholderStrategy
获取方法:\Drupal::service('placeholder_strategy');
该服务是在core.services.yml中定义的,如下:
placeholder_strategy:
class: Drupal\Core\Render\Placeholder\ChainedPlaceholderStrategy
tags:
- { name: service_collector, tag: placeholder_strategy, call: addPlaceholderStrategy }
她按照优先级收集系统中所有具备“placeholder_strategy”标签的服务(占位策略服务),并依次执行,执行逻辑如下:
按优先级依次执行各占位策略服务,数值越大优先级越高,越先执行,在处理链中,前面已经被处理返回的占位符,不会再被传递给后面的占位策略服务,因此优先级很重要,整个过程处理结束后,只返回被处理的占位符。
自定义占位策略服务:
定义一个类,实现以下接口:
\Drupal\Core\Render\Placeholder\PlaceholderStrategyInterface
并将其定义成服务,给出服务标签为:“placeholder_strategy”,并设定一个优先级(整数,可以为负数)。
类方法:processPlaceholders(array $placeholders);接收传入的占位内容,也就是渲染数组中的以下变量:
$element['#attached']['placeholders']
是一个数组,键名为占位token,键值通常为回调,可参见本系列的《渲染占位符产生器》主题
只能返回传入$placeholders的子集,没有被返回的token将传递给后续占位策略服务继续处理,返回的将不会继续传递,在该方法中我们可以对占位内容进行修改;占位策略服务相当于系统针对占位内容派发的钩子。
默认占位策略服务:
系统提供了以下默认占位策略服务,她也是标准安装中唯一的一个占位策略服务:
服务:placeholder_strategy.single_flush
类:Drupal\Core\Render\Placeholder\SingleFlushStrategy
该服务是在core.services.yml中定义的,如下:
placeholder_strategy.single_flush:
class: Drupal\Core\Render\Placeholder\SingleFlushStrategy
tags:
- { name: placeholder_strategy, priority: -1000 }
她不做任何处理,原封不动的直接返回所有还没有被处理的占位内容,因此她应该是最后一个执行的,我们自定义的占位策略服务的优先级不应该低于“-1000”,否则没有效果;她是系统默认定义的唯一一个占位策略服务,这意味着整个占位策略订阅器实际上什么也没有做,只是提供了一个自定义的入口。
补充说明:
1、在默认实现中,返回的占位内容不会继续传递,如果我们既想修改她又想让她继续传递给后续占位策略服务怎么办?你可能会想到参数以引用接收,但这和接口定义是不相容的,因此无法实现这样的需求,也不应该这样实现,这是一种不优雅的做法,应该在内部直接处理到位。
2、占位策略服务通常应该以服务方式提供,在容器编译阶段收集,但也可以在运行时提供,调用如下代码即可:
\Drupal::service('placeholder_strategy')->addPlaceholderStrategy($strategy)
但需要注意:目前(drupal8.5)实现比较简单,该方式不能传递优先级参数,依赖于调用顺序,因此请尽量用服务方式提供。