144.批量更新BulkUpdate

     批量更新(Bulk update)和“批处理API”是不同的概念,“批处理”是将任务分派在多个请求中执行,目的是避免执行大任务时PHP超时,而本文主题“批量更新”是指对一条或多条数据执行某个操作,如批量删除节点、一次性将某权限授予多个用户、批量置顶内容等等,在默认的内容管理页(/admin/content)或用户列表页(/admin/people)就列出了一些操作,选择一些条目和需要应用的操作后点击“应用于选中项(Apply to selected items)”就可以执行选择的操作了,这里被执行的操作也叫做“动作action”,各动作由动作插件提供。

动作插件管理器
服务idplugin.manager.action
类:Drupal\Core\Action\ActionManager
插件目录:src/Plugin/Action
插件接口:Drupal\Core\Action\ActionInterface
动作插件用于实际执行某个动作,即提供执行某个动作的具体逻辑,但不代表实现了某个动作插件就能在系统中直接应用该动作,是否能够运用某动作受控于动作配置实体(见下);动作可分为两大类:

高级动作:
高级动作又称为可配置动作,这种动作的执行需要额外参数,参数的提供时机不是在动作应用页面,而是预设储存在动作配置实体中,在动作配置实体的编辑页面中,插件需提供配置表单来收集所需的参数

简单动作:
和高级动作相比,简单动作不需要额外参数

 

自定义动作插件:

系统提供了以下通用基类:

\Drupal\Core\Action\ActionBase

由于系统主要内容数据均是以实体方式提供,所以许多动作实现也是针对实体的(针对非实体数据的动作见下文),为此进一步提供了如下基类:

\Drupal\Core\Action\Plugin\Action\EntityActionBase

自定义的动作插件通常可以继承自以上基类,放置在模块目录“src/Plugin/Action”中,释文中提供以下关键内容:

id插件id

label给人类看的名称

action_label基本label,有用到默认派生器时需要指定

confirm_form_route_name可选,提供执行动作确认的确认表单路由名

deriver可选,派生器类

type通常是可运用该动作的实体类型ID,但如果动作有特殊用途或不针对实体也可以是自定义值,如“system”,必选,如果一个动作可以运用于多种实体类型,此时通常需要提供派生器为每一个实体类型生成插件定义,提供派生器时在基插件中该项可选,但派生后必须存在

系统默认提供了以下派生器基类:

\Drupal\Core\Action\Plugin\Action\Derivative\EntityActionDeriverBase

自定义的派生器通常会继承该基类,继承后只需要实现“isApplicable”方法以指定动作是否能运用于某实体类型即可

如果自定义的动作是高级动作,即需要提供额外参数的动作,那么插件可继承以下可配置默认基类:

\Drupal\Core\Action\ConfigurableActionBase

该基类实现了以下关键接口:

\Drupal\Core\Plugin\PluginFormInterface

\Drupal\Component\Plugin\ConfigurableInterface

\Drupal\Component\Plugin\ConfigurablePluginInterface

自定义插件可参考以下系统提供的插件:

\Drupal\Core\Action\Plugin\Action\PublishAction

\Drupal\node\Plugin\Action\UnpublishByKeywordNode

 

动作配置实体:

实体类型idaction
实体类:\Drupal\system\Entity\Action
动作配置实体用于控制某动作的启禁用状态,也为高级动作储存所需的配置参数,系统默认建立了一些简单动作的动作配置实体以允许常规功能(如发布、删除等),为高级动作建立动作配置实体则需要启用核心模块:“action”,该模块和动作配置实体的关系类似于视图UI模块和视图实体的关系,用于为动作提供用户操作接口,在默认标准安装中没有启用,启用后将建立菜单:管理>配置>系统>动作,路径如下:

/admin/config/system/actions

该菜单将显示已建立的动作配置实体及添加操作,和视图一样,当配置好动作实体后,可以卸载动作模块而不影响动作使用,下文将假设动作模块处于启用状态。

动作配置实体各处理器设置如下:

            Array
                (
                    [access] => Drupal\Core\Entity\EntityAccessControlHandler
                    [storage] => Drupal\Core\Config\Entity\ConfigEntityStorage
                    [form] => Array
                        (
                            [add] => Drupal\action\Form\ActionAddForm
                            [edit] => Drupal\action\Form\ActionEditForm
                            [delete] => Drupal\action\Form\ActionDeleteForm
                        )
                    [list_builder] => Drupal\action\ActionListBuilder
                )

启用某个动作即是建立一个使用对应动作插件的动作配置实体,取消某动作即是删除或禁用对应的动作配置实体,一个动作插件可对应多个动作配置实体;高级动作所需的配置在动作配置实体编辑表单中设置,系统是否能应用某个动作是面向动作配置实体的,而不面向动作插件,某动作启用后,在动作的应用页面将出现在可选应用动作的选择下拉列表中

 

注意事项:

高级动作插件的表单构建、提交方法的实现:
构建配置表单时,无需考虑配置项在表单中的位置,且提交时保存到插件自己的配置属性即可,这是因为动作配置实体实现了以下接口:

EntityWithPluginCollectionInterface

实现该接口的实体在保存时会将插件的配置保存到特定属性下,这里是“configuration”键,详见:

\Drupal\Core\Config\Entity\ConfigEntityBase::preSave

可参考:\Drupal\node\Plugin\Action\UnpublishByKeywordNode

部分简单动作配置实体的自动维护:
部分简单动作的动作配置实体系统会自动维护,比如添加、移除角色;参考以下函数:

user_user_role_insert(RoleInterface $role)
user_user_role_delete(RoleInterface $role)
system_post_update_change_action_plugins()
system_post_update_change_delete_action_plugins()


动作的调用:

用到动作的页面(动作应用页),比如内容管理页,动作是如何出现在这里的呢?通常是借助视图模块(详见视图篇)。

在视图模块的视图数据钩子(views_views_data())实现中,为每一个有可用动作的实体类型注册了批量更新字段,默认的视图字段插件为:bulk_form,该逻辑如下:


  foreach (\Drupal::entityTypeManager()->getDefinitions() as $entity_type => $entity_info) {
    $actions = array_filter(\Drupal::entityTypeManager()->getStorage('action')->loadMultiple(), function (ActionConfigEntityInterface $action) use ($entity_type) {
      return $action->getType() == $entity_type;
    });
    if (empty($actions)) {
      continue;
    }
    $data[$entity_info->getBaseTable()][$entity_type . '_bulk_form'] = [
      'title' => t('Bulk update'),
      'help' => t('Allows users to apply an action to one or more items.'),
      'field' => [
        'id' => 'bulk_form',
      ],
    ];
  }

页面的渲染会用到以下表单:

\Drupal\views\Form\ViewsForm

如果用户点击应用某动作,默认的执行入口如下:

\Drupal\views\Plugin\views\field\BulkForm::viewsFormSubmit(&$form, FormStateInterface $form_state)

 

在程序中调用动作:

程序代码示例如下:

    $actionEntityID = 'geiyonghuxianshiyitiaoxinxi';
    $actionEntity = \Drupal::entityTypeManager()->getStorage('action')->load($actionEntityID);
    $entities = [1];
    $actionEntity->execute($entities);
    $definition = $actionEntity->getPluginDefinition();
    if (!empty($definition['confirm_form_route_name'])) {
      //重定向页面,继续完成动作
    }

系统默认提供的动作:

取消发布包含关键词的内容:

插件idnode_unpublish_by_keyword_action

类:\Drupal\node\Plugin\Action\UnpublishByKeywordNode

仅能用于节点,只要在完整视图模式中页面会输出包含关键词的内容即取消发布

 

给当前用户显示一个消息:

插件idaction_message_action

类:Drupal\Core\Action\Plugin\Action\MessageAction

该动作一般给程序使用,该插件实现上有bug,调用占位符服务时忘记传递用户上下文数据,因此关于用户的占位符无法替换

 

更改内容的作者:

插件idnode_assign_owner_action

类:\Drupal\node\Plugin\Action\AssignOwnerNode

仅应用于节点,这是一个高级动作,需要在动作实体编辑页中先配置更改到的用户

 

重定向url:

插件idaction_goto_action

类:\Drupal\Core\Action\Plugin\Action\GotoAction

在系统核心响应事件中添加一个实现重定向URL的侦听器,从而实现页面重定向

 

发送邮件:

插件idaction_send_email_action

类:\Drupal\Core\Action\Plugin\Action\EmailAction

向某个邮件地址发送一封邮件,地址和邮件内容在动作配置实体中设置,如果该邮件属于某个账户的,那么会用该账户的首选语言发送,否则采用站点默认语言

 

取消发布包含设定关键词的评论:

插件idcomment_unpublish_by_keyword_action

类:\Drupal\comment\Plugin\Action\UnpublishByKeywordComment

仅能用于评论,只要在评论的完整视图模式中输出会包含关键词,那么该评论即取消发布

 

补充:

1、通过本篇所述知识点,推荐读者尝试实现以下两个功能:

自定义一个批量替换字段内容的动作,参考模块:

https://www.drupal.org/project/views_bulk_edit

https://www.drupal.org/project/bulk_update_fields

https://www.drupal.org/project/views_bulk_operations

自定义一个动作,在用户列表中添加批量发送邮件功能,参考系统默认提供的发送邮件动作

 

2、通常“动作”是作用于实体的,如果是非实体的数据如何运用动作呢?比如一个自定义的数据表,优雅的实现通常需要结合视图模块提供的基础功能,先实现一个该表的批量更新视图字段,修改视图数据定义,再依据这些实现去自定义专用的动作。

本书共152小节。


目前全部收费内容共275.00元。购买全部

评论 (0)