雲客Drupal源碼分析之實體列表構建器EntityListBuilder

實體列表構建和實體視圖構建有相似之處,但並不是一樣的概念,從字面意思看列表構建似乎是用於產生實體的索引頁面(比如摘要列表頁),但產生索引頁面是視圖構建器的工作,而列表構建則是用於管理工作:用來列出實體以便操作它們

列表路由:
比如“管理-結構-內容類型”(地址:/admin/structure/types),這個頁面就是實體列表構建器產生的列表頁面,用於對內容類型進行管理,其路由定義如下:

entity.node_type.collection:
  path: '/admin/structure/types'
  defaults:
    _entity_list: 'node_type'
    _title: 'Content types'
  requirements:
    _permission: 'administer content types'

如果路由中沒有設置控制器,只設置了“_entity_list”,那麼會使用以下通用列表控制器:
   \Drupal\Core\Entity\Controller\EntityListController::listing
這在以下路由增強器中處理
    \Drupal\Core\Entity\Enhancer\EntityRouteEnhancer::enhanceEntityList
如果路由中設置了“_entity_list”,則其值應是實體類型id,表示對該實體類型進行列表操作,在以上通用實體列表控制器中當做參數“$entity_type”的值,以字符串方式傳入
產生實體列表頁的代碼如下:

$listBuilder = \Drupal::entityTypeManager()->getListBuilder($entityTypeID);
return $listBuilder->render();

這調用實體列表構建器並返回列表頁面的渲染數組

實體列表構建器:
列表構建核心是列表構建器,這是實體的一個處理器,定義在實體釋文處理器鍵下的“list_builder”項中,並不是所有實體類型都有列表構建器,比如“entity_view_display”就不需要因此沒有,可通過以下代碼來查看實體類型的列表構建器:

        $definitions = \Drupal::entityTypeManager()->getDefinitions();
        $data = [];
        foreach ($definitions as $entity_type => $definition) {
            $data[$entity_type] = $definition->getHandlerClass('list_builder');
        }
        print_r($data);die;

列表構建器以處理器方式進行實例化,其實現接口如下:
  \Drupal\Core\Entity\EntityListBuilderInterface
默認基類:
  \Drupal\Core\Config\Entity\ConfigEntityListBuilder(配置實體)
  \Drupal\Core\Entity\EntityListBuilder(內容實體)
通常自定義的實體類型需要繼承她們,默認系統中的列表構建器均繼承自她們,實體類型:comment和taxonomy_term甚至直接使用了默認基類

下文分別介紹這兩個默認基類
實體列表構建器默認基類:
類如下:
\Drupal\Core\Entity\EntityListBuilder
方法說明:

public function getStorage()
返回實體的儲存處理器,見本系列儲存處理器主題

public function load()
通過儲存處理器加載要在列表中顯示的實體

protected function getEntityIds()
決定加載哪些實體,返回實體類型id,默認以實體id鍵升序排序,設置每頁加載數量,默認每頁50條

public function getOperations(EntityInterface $entity)
返回實體的操作鏈接,值爲一個數組,以操作名做鍵名,鍵值爲一個數組,有以下鍵值:
    title:翻譯後的標題,做鏈接文字,可爲翻譯對象,如:t('Edit')
    weight:操作排序權重,浮點數
    url:操作鏈接的url對象
該方法會派發以下構建了鉤子:
    entity_operation
函數:
    hook_entity_operation(\Drupal\Core\Entity\EntityInterface $entity)
默認的操作類型和模塊構建的操作類型以“+”合併,默認的優先級更高,合併後再派發該鉤子的修改鉤子

protected function getDefaultOperations(EntityInterface $entity)
返回實體的默認操作,也就是實體釋文中定義的操作,默認有兩個:更新和刪除,需要有權限且有對應的鏈接模板,由該方法可見鏈接模板的鍵名定義需要遵循規範

public function buildHeader()
構建列表頭部標題欄,默認只有一列,子類往往需要重寫該方法以添加更多列數據

public function buildRow(EntityInterface $entity)
構建列表行,默認只有一列,子類往往需要重寫該方法以添加更多列數據

public function buildOperations(EntityInterface $entity)
構建操作單元格中顯示的操作鏈接的渲染數組

public function render()
返回列表頁的主渲染數組,以表格方式渲染(默認只有操作欄一列),附加分頁鏈接

protected function ensureDestination(Url $url)
爲操作鏈接附加目的地“destination”查詢參數,以便操作後的調跳轉

配置實體列表構建器默認基類:
該類繼承自前一節的默認基類,以特別處理配置實體,類如下:
   \Drupal\Core\Config\Entity\ConfigEntityListBuilder
方法解釋如下:
public function load()
加載未經配置覆寫的配置實體,和內容實體以實體id排序不同,配置實體以實體類的sort()方法排序

public function getDefaultOperations(EntityInterface $entity)
配置實體的默認操作鏈接比內容實體多出啓用禁用鏈接,在該方法中特別處理


子類例舉:
以上基類只顯示了列表中的一個列(操作列),併爲提供其他列的顯示,因此子類需要添加更多的列,通常需要實現以下方法:
添加頭部其他列數據:
  public function buildHeader()
添加每行其他列數據:
  public function buildRow(EntityInterface $entity)

可參考內容類型的列表構建器:
  \Drupal\node\NodeTypeListBuilder
  \Drupal\node\NodeListBuilder

處理重定向目的地:
在drupal的網址中GET參數:“destination”(目的地)具有特殊含義,指示請求處理完成後跳轉到這個參數值所示的網址,根據該參數的值分幾種情況:
  如果存在但指向外部站點將禁止跳轉,而重定向到網站首頁
  如果存在且爲網站內部地址將正常跳轉
  如果不存在將跳轉到當前頁
該邏輯被大量使用,因此係統定義了重定向目的地服務以便於使用,定義如下:
容器id:redirect.destination
類:Drupal\Core\Routing\RedirectDestination
獲取方法:\Drupal::destination();
方法如下:
public function get()
以字符串方式返回處理後的目的地鏈接

public function set($new_destination)
設置一個目的地鏈接,參數爲字符串

public function getAsArray()
返回一個數組,鍵名爲“destination”,鍵值爲字符串方式的目的地鏈接,該返回值通常用於給url對象設置選項值,如:
   $url->mergeOptions(['query' =>\Drupal::destination()->getAsArray()]);
這將給url的查詢參數添加上“destination”參數


補充:
1、實體對象的toUrl方法可以依據實體釋文中的鏈接模板(links鍵)產生指向自己的某種操作鏈接(URL對象),如查看、編輯等,我們知道有鏈接是不夠的,還需要路由定義,那麼鏈接模板和路由之間是什麼樣的關係呢?你可能會想到讓系統自動依據鏈接模板定義路由,這是可行的,但目前系統尚未實現這個功能,需要由實體類型所屬模塊自行定義,這就需要模塊開發者明確知道二者的對應規範了,首先路徑的格式需要相同,其次假設鏈接模板的鍵名爲$rel,路由名和$rel有如下對應關係:

$route_name = "entity.{$entityTypeId}." . str_replace(['-', 'drupal:'], ['_', ''], $rel);

如節點實體鏈接模板中的編輯表單$rel 爲:edit-form,路由名就是entity.node.edit_form
再次就是路由變量問題,變量名和其含義對應如下:
實體類型id:實體id,如“node:28”
實體類型id_revision:版本id,如“node_revision:28”
bundle實體類型名:bundle類型值,如“node_type:article”,bundle實體類型名以釋文中的bundle_entity_type優先,如果不存在將以標準實體鍵bundle的值爲準

2、實體鏈接模板中的鍵名$rel也有標準規範,不可隨意定義,如編輯表單應爲“edit-form”,刪除表單應爲“delete-form”,否則像列表構建器這樣的組件將無法正確處理

 

我是雲客,【雲遊天下,做客四方】,聯繫方式見主頁,歡迎轉載,但須註明出處

 

 

 

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章