1. 程式人生 > >雲客Drupal8原始碼分析之臨時儲存與訊息服務

雲客Drupal8原始碼分析之臨時儲存與訊息服務

前言:臨時儲存與訊息服務之間並沒有什麼直接關聯,由於她們都是系統基礎元件,內容也比較簡單,為後續主題做準備,所以放在一起講解。

臨時儲存概述:
臨時儲存用來儲存一些臨時性的資料,超期後會被刪除,比如節點在儲存前的預覽資料,她和快取不一樣,她是臨時性的、不能被重建的資料,依據被儲存的資料在使用者間能否互相訪問又分為私有和共享。

私有臨時儲存:
使用者之間不共享資料,只能訪問或操作自己的資料,已登入使用者使用使用者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();
移除後此程式碼會改為返回訊息服務物件

我是雲客,【雲遊天下,做客四方】,微訊號:php-world,歡迎轉載,但須註明出處,討論請加qq群203286137