跳到内容

批量操作

编辑此页

批量操作是对一组选定对象触发的操作。默认情况下,管理员具有 delete 操作,允许您一次删除多个条目。

定义新的操作

要创建新的自定义批量操作,使其出现在列表视图中,请按照以下步骤操作

在您的 Admin 类中覆盖 configureBatchActions(),通过将新的批量操作添加到 $actions 数组来定义它们。每个键代表一个批量操作,可以包含以下设置

  • label: 当向用户提供此选项时使用的名称,应通过翻译器传递 (默认值:标签通过 labelTranslatorStrategy 生成)
  • translation_domain: 用于翻译键的域。(默认值:管理员的翻译域)
  • ask_confirmation: 默认为 true,表示在处理批量操作之前,将要求用户确认
  • template: 覆盖此特定操作的 ask_confirmation 模板。这允许您为每个需要确认的批量操作指定不同的模板。

例如,让我们定义一个新的 merge 操作,该操作接受多个源项目并将它们合并到一个目标项目上。它应该仅在满足两个条件时可用

  • 此管理员存在 EDIT 和 DELETE 路由(尚未禁用)
  • 已登录的管理员具有 EDIT 和 DELETE 权限

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    protected function configureBatchActions(array $actions): array
    {
        if (
          $this->hasRoute('edit') && $this->hasAccess('edit') &&
          $this->hasRoute('delete') && $this->hasAccess('delete')
        ) {
            $actions['merge'] = [
                'ask_confirmation' => true,
                'controller' => 'app.controller.merge::batchMergeAction',
                // Or 'App/Controller/MergeController::batchMergeAction' base on how you declare your controller service.
            ];
        }
    
        return $actions;
    }

定义核心操作逻辑

像往常一样定义一个常规 Symfony 控制器(没有路由)。确保将您的控制器配置为服务,并使用 controller.service_arguments 标记它。参数将自动注入。AdminInterface 通过 SonataAdminBundle 中已有的参数转换器完成。$query 对于此请求的上下文是唯一的。对基类或任何其他逻辑没有要求,这只是一个示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
// src/Controller/MergeController.php

namespace App\Controller;

use Sonata\AdminBundle\Admin\AdminInterface;
use Sonata\AdminBundle\Datagrid\ProxyQueryInterface;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\RedirectResponse;
use Symfony\Component\HttpFoundation\Request;

class MergeController extends AbstractController
{
    public function batchMergeAction(ProxyQueryInterface $query, AdminInterface $admin): RedirectResponse
    {
        $admin->checkAccess('edit');
        $admin->checkAccess('delete');

        $modelManager = $admin->getModelManager();

        $target = $modelManager->find($admin->getClass(), $request->get('targetId'));

        if ($target === null) {
            $this->addFlash('sonata_flash_info', 'flash_batch_merge_no_target');

            return new RedirectResponse(
                $admin->generateUrl('list', [
                    'filter' => $admin->getFilterParameters()
                ])
            );
        }

        $selectedModels = $query->execute();

        // do the merge work here

        try {
            foreach ($selectedModels as $selectedModel) {
                $modelManager->delete($selectedModel);
            }

            $this->addFlash('sonata_flash_success', 'flash_batch_merge_success');
        } catch (\Exception $e) {
            $this->addFlash('sonata_flash_error', 'flash_batch_merge_error');
        } finally {
            return new RedirectResponse(
                $admin->generateUrl('list', [
                    'filter' => $admin->getFilterParameters()
                ])
            );
        }
    }

    // ...
}

(已弃用) 定义核心操作逻辑

已弃用: 这是执行此操作的旧方法。将在 5.x 版本中删除。

方法 batchAction<MyAction> 将在您的 CRUDController 类中执行,以处理您的批量操作。选定的对象通过查询参数传递给此方法,该参数可用于检索它们。如果出于某种原因,在没有默认选择方法的情况下执行批量操作是有意义的(例如,您在模板级别定义了另一种以更低粒度选择模型的方式),则传递的查询为 null

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
// src/Controller/CRUDController.php

namespace App\Controller;

use Sonata\AdminBundle\Controller\CRUDController as BaseController;
use Sonata\AdminBundle\Datagrid\ProxyQueryInterface;
use Symfony\Component\HttpFoundation\RedirectResponse;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\Security\Core\Exception\AccessDeniedException;

class CRUDController extends BaseController
{
    /**
     * @param ProxyQueryInterface $selectedModelQuery
     * @param Request             $request
     *
     * @return RedirectResponse
     */
    public function batchActionMerge(ProxyQueryInterface $selectedModelQuery, Request $request)
    {
        $this->admin->checkAccess('edit');
        $this->admin->checkAccess('delete');

        $modelManager = $this->admin->getModelManager();

        $target = $modelManager->find($this->admin->getClass(), $request->get('targetId'));

        if ($target === null){
            $this->addFlash('sonata_flash_info', 'flash_batch_merge_no_target');

            return new RedirectResponse(
                $this->admin->generateUrl('list', [
                    'filter' => $this->admin->getFilterParameters()
                ])
            );
        }

        $selectedModels = $selectedModelQuery->execute();

        // do the merge work here

        try {
            foreach ($selectedModels as $selectedModel) {
                $modelManager->delete($selectedModel);
            }

            $modelManager->update($selectedModel);
        } catch (\Exception $e) {
            $this->addFlash('sonata_flash_error', 'flash_batch_merge_error');

            return new RedirectResponse(
                $this->admin->generateUrl('list', [
                    'filter' => $this->admin->getFilterParameters()
                ])
            );
        }

        $this->addFlash('sonata_flash_success', 'flash_batch_merge_success');

        return new RedirectResponse(
            $this->admin->generateUrl('list', [
                'filter' => $this->admin->getFilterParameters()
            ])
        );
    }

    // ...
}

注意

您可以在架构部分查看如何声明您自己的 CRUDController 类。

(可选) 重写批量选择模板

合并操作需要两种选择:一组要从中合并的源对象和一个要合并到的目标对象。默认情况下,batch_actions 只允许您选择一组要操作的对象。我们可以通过更改列表模板 (list__batch.html.twig) 并添加一个单选按钮来选择目标对象来覆盖此行为。

1
2
3
4
5
6
7
8
9
10
11
12
{# templates/bundles/SonataAdminBundle/CRUD/list__batch.html.twig #}

{# see @SonataAdmin/CRUD/list__batch.html.twig for the current default template #}

{% extends get_admin_template('base_list_field', admin.code) %}

{% block field %}
    <input type="checkbox" name="idx[]" value="{{ admin.id(object) }}"/>

    {# the new radio button #}
    <input type="radio" name="targetId" value="{{ admin.id(object) }}"/>
{% endblock %}

(可选|已弃用) 重写默认的相关性检查函数

已弃用: 直接在您的控制器中进行此检查。这将在 5.x 版本中删除

默认情况下,如果没有选择任何对象,则不会执行批量操作,并且用户将收到未选择的通知。如果您的自定义批量操作需要更复杂的逻辑来确定是否可以执行操作,请在您的 CRUDController 类中定义一个 batchAction<MyAction>IsRelevant 方法(例如 batchActionMergeIsRelevant)。此检查在要求用户确认之前执行,以确保实际上有需要确认的内容。

此方法可能返回三个不同的值

  • true: 批量操作是相关的并且可以应用。
  • false: 与上面相同,带有默认的“操作已中止,未选择模型”通知消息。
  • string: 给定当前请求参数,批量操作不相关(例如,merge 操作缺少 target)。返回的字符串是显示给用户的消息

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    // src/Controller/CRUDController.php
    
    namespace App\Controller;
    
    use Sonata\AdminBundle\Controller\CRUDController as BaseController;
    use Symfony\Component\HttpFoundation\Request;
    
    class CRUDController extends BaseController
    {
        public function batchActionMergeIsRelevant(array $selectedIds, $allEntitiesSelected, Request $request)
        {
            // here you have access to all POST parameters, if you use some custom ones
            // POST parameters are kept even after the confirmation page.
            $parameterBag = $request->request;
    
            // check that a target has been chosen
            if (!$parameterBag->has('targetId')) {
                return 'flash_batch_merge_no_target';
            }
    
            $targetId = $parameterBag->get('targetId');
    
            // if all entities are selected, a merge can be done
            if ($allEntitiesSelected) {
                return true;
            }
    
            // filter out the target from the selected models
            $selectedIds = array_filter($selectedIds,
                function($selectedId) use($targetId){
                    return $selectedId !== $targetId;
                }
            );
    
            // if at least one but not the target model is selected, a merge can be done.
            return count($selectedIds) > 0;
        }
    }

(可选) 执行预批量钩子

在您的 admin 类中,您可以创建一个 preBatchAction 方法,以便在执行批量操作之前执行某些操作。此方法的主要目的是更改查询或选定 ID 的列表

1
2
3
4
5
6
7
8
9
10
public function preBatchAction($actionName, ProxyQueryInterface $query, array &$idx, bool $allElements): void
{
    // altering the query or the idx array
    $foo = $query->getParameter('foo')->getValue();

    // Doing something with the foo object
    // ...

    $query->setParameter('foo', $bar);
}
这项工作,包括代码示例,根据 Creative Commons BY-SA 3.0 许可协议获得许可。
目录
    版本