88. 临时储存与消息服务
临时储存与消息服务
前言:临时储存与消息服务之间并没有什么直接关联,由于她们都是系统基础组件,内容也比较简单,为后续主题做准备,所以放在一起讲解。
临时储存概述:
临时储存用来储存一些临时性的数据,超期后会被删除,比如节点在保存前的预览数据,她和缓存不一样,她是临时性的、不能被重建的数据,依据被储存的数据在用户间能否互相访问又分为私有和共享。
私有临时储存:
用户之间不共享数据,只能访问或操作自己的数据,已登录用户使用用户id(整数)做隔离,未登录用户使用会话id做隔离。
服务id: tempstore.private
类:Drupal\Core\TempStore\PrivateTempStoreFactory
服务定义:
tempstore.private:
class: Drupal\Core\TempStore\PrivateTempStoreFactory
arguments: ['@keyvalue.expirable', '@lock', '@current_user', '@request_stack', '%tempstore.expire%']
tags:
- { name: backend_overridable }
该服务使用了有期限限制的键值对储存服务(详请见本系列相关介绍),服务id:keyvalue.expirable,在默认情况下最终会使用有期限限制的数据库键值对储存服务(服务id:keyvalue.expirable.database),此默认行为可以在站点服务配置文件(\sites\default\ services.yml)中更改,配置键:factory.keyvalue.expirable,其值为一个数组,键名是集名,键值为该集要使用的键值储存服务的id,为所有集名指定默认服务时使用keyvalue_expirable_default做键名,如果不进行配置则系统默认使用服务keyvalue.expirable.database,在底层数据库中,键值储存系统传入的集名追加了前缀:“tempstore.private.”。
锁服务用以确保在多个请求同时执行某个操作时只有一个请求能够执行,详见本系列锁服务主题
临时储存超期时间默认为7天,这在核心服务定义文件(core.services.yml)中以秒指定:
tempstore.expire: 604800
在站点服务配置文件里面可以修改默认值,服务参数设置里添加修改即可,单位为秒
通过以下代码,我们可以得到私有临时储存对象,在该对象上进行私有临时数据的操作:
$store = \Drupal::service(' tempstore.private')-> get($collection);
该私有临时储存对象类定义为:
\Drupal\Core\TempStore\PrivateTempStore
通过该类可见系统使用了用户id或会话id在键值集的键名中,以确保代码只能操作当前用户的数据,换句话说用户只能操作自己的数据,可用方法如下:
$store->get($key);
得到数据
$store->set($key, $value)
设置并保存数据,会使用锁以排斥并发操作,值可以是标量数据、数组、对象等
$store->getMetadata($key)
得到数据的元数据,返回一个数组转化的标准对象,只有两个属性:owner的值是用户id或会话id,updated是数据被创建时的主请求的请求时间戳
$store->delete($key)
删除数据,会使用锁以排斥并发操作
共享临时储存:
和私有临时储存不同,共享临时储存允许用户去操作其他用户的数据,定义如下:
服务id:tempstore.shared
类:Drupal\Core\TempStore\SharedTempStoreFactory
定义:
tempstore.shared:
class: Drupal\Core\TempStore\SharedTempStoreFactory
arguments: ['@keyvalue.expirable', '@lock', '@request_stack', '%tempstore.expire%']
tags:
- { name: backend_overridable }
和私有临时储存一样,同样使用了有时间期限限制的键值储存系统、相同的超期时间、锁服务,这里不再赘述,在底层键值集使用“tempstore.shared.”做前缀,使用方法如下:
$store = \Drupal::service(' tempstore.private')-> get($collection, $owner = NULL);
参数$owner是被操作的共享数据的所有者,值为用户id或会话id,如果省略将使用当前用户id或会话id
该代码返回共享临时储存对象,类定义如下:
\Drupal\Core\TempStore\SharedTempStore
从该类可见,任意用户可以使用相同的key去存储或取回数据,这不像私有临时储存那样每个用户的key值都不一样,意味着只要有key值就能获取共享的数据,从而实现数据共享,但依然会将用户信息储存在元数据中,这会带来一些特殊用途,共享临时储存对象可用方法如下:
$store->get($key);
得到数据,而不管所有者是谁
$store->getIfOwner($key)
得到数据,如果所有者不是传入的所有者,那么返回空值
$store->set($key, $value)
以传入的所有者的名义保存数据
$store->setIfNotExists($key, $value)
在数据不存在时以传入的所有者的名义保存数据
$store->setIfOwner($key, $value)
在数据不存在时以传入的所有者的名义保存数据,或者数据存在且所有者和传入的所有者相同时才保存数据
$store->getMetadata($key)
得到数据的元数据,返回一个数组转化的标准对象,只有两个属性:owner的值是数据所有者的用户id或会话id,updated是数据被创建时主请求的请求时间戳
$store->delete($key)
删除数据,而不管所有者是谁,会使用锁以排斥并发操作
$store->deleteIfOwner($key)
删除数据,只有数据所有者和传入的所有者相同时才删除
消息服务:
消息服务用于在页面之间传递运行时消息,比如表单验证错误时的页面提示、上一个页面的操作结果提示等等,并非用户与用户之间互发的站内消息,而是同一个用户的跨页面系统提示消息,通常只显示一次,然后删除,这将替换原来的以下函数:
drupal_set_message($message = NULL, $type = 'status', $repeat = FALSE)
drupal_get_messages($type = NULL, $clear_queue = TRUE)
这两个消息函数在drupal8.5版本中弃用,9.0时会移除。
消息服务定义如下:
messenger:
class: Drupal\Core\Messenger\Messenger
arguments: ['@session.flash_bag', '@page_cache_kill_switch']
这是建立在会话闪存包对象上的,详见本系列会话相关主题,闪存包对象有个鲜明特点就是用后即毁,但也提供peek类方法(偷瞥一眼方法,即取回数据后不损毁数据),消息分为三类:状态、警告、错误,所有的消息都储存在这三类下,获取消息服务如下:
$messenger = \Drupal::service('messenger');
一般来说这样的获取方法用在钩子、静态方法等地方,如果是自定义服务,最佳做法是以参数传入,消息对象有以下可用方法:
$messenger->addError($message, $repeat = FALSE)
添加错误消息,参数$message可以是字符串,也可以是\Drupal\Core\Render\Markup对象,在内部使用Markup对象保存,添加消息时最佳实践是以大写字母开头并以句点结束,注意我们应该使用t函数翻译之后再传入,她内部不会自动翻译;参数$repeat表示如果消息已经存在,是否重复添加,默认不重复
$messenger->addStatus($message, $repeat = FALSE)
添加状态消息,同上
$messenger->addWarning($message, $repeat = FALSE)
添加警告消息,同上
$messenger->addMessage($message, $type = self::TYPE_STATUS, $repeat = FALSE)
按类型添加消息,以上三个方法均使用该方法,很少直接使用,当直接调用该方法时,类型请按常量传递是个好习惯,从该方法可知有消息提示的页面不会被缓存
$messenger->all()
查看所有消息,但不删除消息,返回一个数组,键名是消息类型,键值是消息构成的索引数组
$messenger->messagesByType($type)
按类型查看消息,但不删除,返回一个由该类型消息构成的数组
$messenger->deleteAll()
删除并返回全部消息
$messenger->deleteByType($type)
按类型删除消息,返回被删除类型的全部消息
程序可以调用以上方法去操作消息,系统会使用消息块去显示被添加的消息,因为闪存包的用后即毁特性,所以这些消息只显示一次,如果页面没有添加消息区块(块id:system_messages_block)则不会显示,具体代码请见:
\Drupal\system\Plugin\Block\SystemMessagesBlock
\Drupal\Core\Render\Element\StatusMessages
为了使用方便,系统还提供了一个消息服务特征:
\Drupal\Core\Messenger\MessengerTrait
当我们的类需要消息服务时可以引用该特征
为了向后兼容,系统提供了一个过渡消息类:
\Drupal\Core\Messenger\LegacyMessenger
在该类中,在有消息服务存在时将使用消息服务,否则采用以前的办法(引用会话变量);这将在drupal9.0移除,不应该直接实例化她,而应该使用以下代码代替:
\Drupal::messenger();
移除后此代码会改为返回消息服务对象