104. 实体列表构建器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(内容实体)

通常自定义的实体类型需要继承她们,默认系统中的列表构建器均继承自她们,实体类型:commenttaxonomy_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(目的地)具有特殊含义,指示请求处理完成后跳转到这个参数值所示的网址,根据该参数的值分几种情况:

如果存在但指向外部站点将禁止跳转,而重定向到网站首页

如果存在且为网站内部地址将正常跳转

如果不存在将跳转到当前页

该逻辑被大量使用,因此系统定义了重定向目的地服务以便于使用,定义如下:

容器idredirect.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”,否则像列表构建器这样的组件将无法正确处理

 

 

本书共123小节:

评论 (写第一个评论)