1. 程式人生 > >雲客Drupal8原始碼分析之主題協商theme negotiator

雲客Drupal8原始碼分析之主題協商theme negotiator

drupal主題系統十分靈活,你可以全站使用一套主題,用響應式設計去相容移動端和pc端,如果響應式無法滿足要求,你可以在各端分別使用不同的主題,但其靈活性遠不止如此,實際上在同一個站點中你可以根據任意條件使用不同的主題,系統後臺設定的只是預設值而已,比如在pc端或移動端你可以為不同語言採用不同主題,甚至不同使用者採用不同模板,條件是任意的,只需要簡單實現一個主題協商即可,這就是本篇所講的內容。

在drupal中我們可以安裝多個主題,前端和管理介面可以各自指定一個預設主題(前端預設為bartik,管理介面為seven),但並不意味著只能使用被指定的預設主題,在站點中可以同時使用多個主題,只要她們被安裝了,那麼一個頁面應該使用哪個主題呢?決定一個頁面使用什麼主題是由主題協商者完成的,她是系統定義的一個服務,實際上系統中有許多個主題協商者,她們按優先順序排序形成一個佇列,依次執行,一旦有協商者做出決定那麼將忽略後續協商者,模組可以自定義主題協商者,在其中我們可以通過任意條件指定任意頁面的主題,比如前文提到的pc端和移動端分別開發主題時,我們就可以根據瀏覽器的代理標誌來判斷使用哪個主題。

注意:這裡需要注意主題和模板之間的區別,可以簡單的這樣理解:主題是一套模板的集合,從系統角度看一個主題是一個擴充套件,除了模板還有很多函式、js、css等等;主題協商是在主題選擇層面工作的,在一個主題內使用哪個模板那是模板鉤子建議的事情(本系列其他文章介紹),一個主題內所有頁面模板風格應該是一致的,不同主題風格往往不同,這不是一個概念。

自定義主題協商者:


這就是在定義一個服務,先定義一個類,放置在名字空間對應的檔案中,該類必須實現以下介面:
\Drupal\Core\Theme\ThemeNegotiatorInterface
該介面有兩個方法:
applies(RouteMatchInterface $route_match):
根據情況決定是否需要使用本協商者,返回布林值,如果返回fasle那麼主題協商的工作將交由隨後的主題協商者來完成,系統預設會傳遞當前主請求的路由匹配器物件,但該方法內部我們也可以根據其他資訊來做決定
determineActiveTheme(RouteMatchInterface $route_match):
返回主題的機器名(注意是機器名),也可以返回NULL表示協商失敗,這將使協商工作讓給優先順序低的隨後主題協商者

注意:在主題協商者中我們無需實現對主題的許可權檢查,該工作將由“access_check.theme”服務去完成(見後文)。

定義好類後,在MODULE.services.yml中將她宣告為服務,見下,重點是給出“theme_negotiator”標籤和優先順序屬性,在容器編譯階段系統會自動收集她並按照優先順序排序。

系統提供的主題協商者:
系統預設提供了六個主題協商者,可以參考她們的服務定義和實現,以下是按照優先順序從高到底排序:
ajax基本頁:
基於POST變數中的ajax_page_state變數,她是一個數組,鍵名含'theme'(主題機器名)和'theme_token'(驗證令牌),只要令牌正確將使用'theme'指定的主題,服務定義如下:

  theme.negotiator.ajax_base_page:
    class: Drupal\Core\Theme\AjaxBasePageNegotiator
    arguments: ['@csrf_token', '@config.factory', '@request_stack']
    tags:
      - { name: theme_negotiator, priority: 1000 }
塊管理demo頁:
只針對“block.admin_demo”路由,採用該路由中路由引數'theme'指定的主題,服務定義如下:
  theme.negotiator.block.admin_demo:
    class: Drupal\block\Theme\AdminDemoNegotiator
    tags:
      - { name: theme_negotiator, priority: 1000 }
系統批處理頁面:
只針對“system.batch_page”路由,服務定義如下:
  theme.negotiator.system.batch:
    class: Drupal\system\Theme\BatchNegotiator
    arguments: ['@batch.storage', '@request_stack']
    tags:
      - { name: theme_negotiator, priority: 1000 }
系統資料庫更新頁:
只針對'system.db_update'路由,用於資料庫維護更新時使用,可以在站點配置檔案中使用配置項“maintenance_theme”指定一個主題,如沒有設定該配置項將採用'seven',如果設定為false將使用預設前端主題,服務定義如下:
  theme.negotiator.system.db_update:
    class: Drupal\system\Theme\DbUpdateNegotiator
    arguments: ['@config.factory']
    tags:
      - { name: theme_negotiator, priority: 100 }
管理(後端)頁主題:
使用者有'view the administration theme'許可權,且訪問的是管理頁(路由中有'_admin_route'選項),那麼將使用管理主題,服務定義如下:
  theme.negotiator.admin_theme:
    class: Drupal\user\Theme\AdminNegotiator
    arguments: ['@current_user', '@config.factory', '@entity.manager', '@router.admin_context']
    tags:
      - { name: theme_negotiator, priority: -40 }
預設主題:
這是優先順序最低的協商者,如果沒有其他協商者進行有效協商時總是被使用,她總是返回前端預設主題,服務定義如下:
  theme.negotiator.default:
    class: Drupal\Core\Theme\DefaultNegotiator
    arguments: ['@config.factory']
    tags:
      - { name: theme_negotiator, priority: -100 }
通過以上系統提供的六個預設協商者,可以看到最高的優先順序是1000,預設協商者優先順序最低,為-100,我們自定義的協商者優先順序需要參考她們以進行恰當的排序,當高優先順序作出有效協商後,低優先順序將被忽略,最低不能低於-100,否則將使用預設主題。
比如前文提到的選擇pc端和移動端主題的協商者就可以定義在-40到-100之間,這樣管理介面不受影響,前端將在各端使用不同主題。

代理協商者:
在系統中對所有協商者的應用是在一個代理中完成的,她的服定義如下:
服務id:theme.negotiator
類:\Drupal\Core\Theme\ThemeNegotiator
在其中可以看到優先順序的處理邏輯,她執行了主題訪問檢查。

主題訪問檢查:
服務id:access_check.theme
類:Drupal\Core\Theme\ThemeAccessCheck
預設檢查僅僅是基於主題是否被安裝,如果還沒有被安裝那麼不允許使用。如有需要,我們可以在模組中定義自己的服務去覆寫該訪問檢查器以實現高階特殊的檢查。
我是雲客,【雲遊天下,做客四方】,微訊號:php-world,歡迎轉載,但須註明出處,討論請加qq群203286137