跳到内容

创建自定义管理操作

编辑此页

这是一个为 SonataAdmin 创建自定义列表操作的完整工作示例。该示例基于 App 命名空间中现有的 CarAdmin 类。假设您已经有一个正在运行的管理服务。

方法

SonataAdmin 提供了一种非常直接的方式来添加您自己的自定义操作。

为此,我们需要

  • 扩展 SonataAdmin:CRUD 控制器,并告知我们的管理类使用它
  • 在我们的控制器中创建自定义操作
  • 创建一个模板以在列表视图中显示该操作
  • 在管理类中添加路由和新操作

扩展管理控制器

首先,您需要创建自己的控制器,扩展 SonataAdmin 的控制器

1
2
3
4
5
6
7
8
9
10
// src/Controller/CarAdminController.php

namespace App\Controller;

use Sonata\AdminBundle\Controller\CRUDController;

class CarAdminController extends CRUDController
{
    // ...
}

默认情况下,管理类使用 SonataAdmin:CRUD 控制器,这是管理服务定义的第三个参数,您需要将其更改为您自己的控制器。

将管理类注册为服务

可以使用 XML

1
2
3
4
5
<!-- config/services.xml -->

<service id="app.admin.car" class="App\Admin\CarAdmin">
    <tag name="sonata.admin" model_class="App\Entity\Car" controller="App\Controller\CarAdminController" manager_type="orm" group="Demo" label="Car"/>
</service>

或将其添加到您的 services.yaml

1
2
3
4
5
6
7
# config/services.yaml

services:
    app.admin.car:
        class: App\Admin\CarAdmin
        tags:
            - { name: sonata.admin, model_class: App\Entity\Car, controller: App\Controller\CarAdminController, manager_type: orm, group: Demo, label: Car }

有关服务配置的更多信息,请参阅 创建管理类 的步骤 3

在您的控制器中创建自定义操作

现在是时候在这里实际创建您的自定义操作了,对于此示例,我选择实现一个 clone 操作

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
// src/Controller/CarAdminController.php

namespace App\Controller;

use Sonata\AdminBundle\Controller\CRUDController;
use Symfony\Component\HttpFoundation\RedirectResponse;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;

class CarAdminController extends CRUDController
{
    /**
     * @param $id
     */
    public function cloneAction($id): Response
    {
        $object = $this->admin->getSubject();

        if (!$object) {
            throw new NotFoundHttpException(sprintf('unable to find the object with id: %s', $id));
        }

        // Be careful, you may need to overload the __clone method of your object
        // to set its id to null !
        $clonedObject = clone $object;

        $clonedObject->setName($object->getName().' (Clone)');

        $this->admin->create($clonedObject);

        $this->addFlash('sonata_flash_success', 'Cloned successfully');

        return new RedirectResponse($this->admin->generateUrl('list'));
    }
}

如果您想将当前过滤器参数添加到重定向 URL,您可以将它们添加到 generateUrl() 方法中

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

在这里,我们首先获取对象,查看它是否存在,然后克隆它并将克隆作为新对象插入。最后,我们设置一个闪存消息指示成功,并重定向到列表视图。

提示

如果您想在此处渲染某些内容,您可以创建任何新模板,扩展 sonata 布局并使用 sonata_admin_content 代码块。

1
2
3
4
5
{% extends '@SonataAdmin/standard_layout.html.twig' %}

{% block sonata_admin_content %}
    Your content here
{% endblock %}

为新操作创建模板

您需要告诉 SonataAdmin 如何渲染您的新操作。您可以通过在您的自定义管理控制器的命名空间中创建一个 list__action_clone.html.twig 来做到这一点。

1
2
3
{# templates/CRUD/list__action_clone.html.twig #}

<a class="btn btn-sm" href="{{ admin.generateObjectUrl('clone', object) }}">clone</a>

现在 clone 不是已知的路由,我们在下一步中定义它。

整合在一起

现在剩下的是将您的自定义操作添加到管理类中。

您必须在 configureRoutes 中添加新路由

1
2
3
4
5
6
7
use Sonata\AdminBundle\Route\RouteCollectionInterface;

protected function configureRoutes(RouteCollectionInterface $collection): void
{
    $collection
        ->add('clone', $this->getRouterIdParameter().'/clone');
}

这为我们提供了一个类似 ../admin/app/car/1/clone 的路由。您也可以编写 $collection->add('clone'); 以获得类似 ../admin/app/car/clone?id=1 的路由

接下来,我们必须在 configureListFields 中添加操作,指定我们创建的模板

1
2
3
4
5
6
7
8
9
10
11
12
13
14
protected function configureListFields(ListMapper $list): void
{
    $list
        ->add(ListMapper::NAME_ACTIONS, null, [
            'actions' => [

                // ...

                'clone' => [
                    'template' => '@App/CRUD/list__action_clone.html.twig',
                ],
            ],
        ]);
}

完整的 CarAdmin.php 示例看起来像这样

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
// src/Admin/CarAdmin.php

namespace App\Admin;

use Sonata\AdminBundle\Admin\AbstractAdmin;
use Sonata\AdminBundle\Datagrid\ListMapper;
use Sonata\AdminBundle\Route\RouteCollection;

final class CarAdmin extends AbstractAdmin
{
    protected function configureRoutes(RouteCollectionInterface $collection): void
    {
        $collection
            ->add('clone', $this->getRouterIdParameter().'/clone');
    }

    protected function configureListFields(ListMapper $list): void
    {
        $list
            ->addIdentifier('name')
            ->add('engine')
            ->add('rescueEngine')
            ->add('createdAt')
            ->add(ListMapper::NAME_ACTIONS, null, [
                'actions' => [
                    'show' => [],
                    'edit' => [],
                    'delete' => [],
                    'clone' => [
                        'template' => '@App/CRUD/list__action_clone.html.twig',
                    ]
                ]
            ]);
    }
}

注意

如果您想通过在 twig 中使用 render 函数在模板中渲染自定义控制器操作,则需要添加 _sonata_admin 作为属性。例如; {{ render(controller('App\\Controller\\XxxxCRUDController::comment', {'_sonata_admin': 'sonata.admin.xxxx' })) }}。这样做是因为在应该发生渲染的那一刻,通常设置此参数值的路由根本没有参与,然后您将收到错误“控制器 AppControllerXxxxCRUDController 和当前路由 '' 没有定义 _sonata_admin。”

没有实体的自定义操作

创建未连接到实体的操作也是可能的。让我们想象我们有一个导入操作。我们注册我们的路由

1
2
3
4
5
6
use Sonata\AdminBundle\Route\RouteCollectionInterface;

protected function configureRoutes(RouteCollectionInterface $collection): void
{
    $collection->add('import');
}

和控制器操作

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// src/Controller/CarAdminController.php

namespace App\Controller;

use Sonata\AdminBundle\Controller\CRUDController;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;

final class CarAdminController extends CRUDController
{
    public function importAction(Request $request): Response
    {
        // do your import logic
    }

现在,我们可以将操作添加到添加按钮旁边,而不是将其添加到表单映射器。在您的管理类中,覆盖 configureActionButtons 方法

1
2
3
4
5
6
protected function configureActionButtons(array $buttonList, string $action, ?object $object = null): array
{
    $buttonList['import'] = ['template' => 'import_button.html.twig'];

    return $buttonList;
}

为该按钮创建一个模板

1
2
3
4
5
<li>
    <a class="sonata-action-element" href="{{ admin.generateUrl('import') }}">
        <i class="fas fa-level-up-alt"></i> {{ 'import_action'|trans({}, 'SonataAdminBundle') }}
    </a>
</li>

您也可以将此操作添加到您的仪表板操作中,您必须覆盖管理类中的 getDashboardActions 方法,并且有两种方法可以添加操作

1
2
3
4
5
6
protected function configureDashboardActions(array $actions): array
{
    $actions['import'] = ['template' => 'import_dashboard_button.html.twig'];

    return $actions;
}

为该按钮创建一个模板

1
2
3
<a class="btn btn-link btn-flat" href="{{ admin.generateUrl('import') }}">
    <i class="fas fa-level-up-alt"></i> {{ 'import_action'|trans({}, 'SonataAdminBundle') }}
</a>

或者您可以将值作为数组传递

1
2
3
4
5
6
7
8
9
10
11
protected function configureDashboardActions(array $actions): array
{
    $actions['import'] = [
        'label' => 'import_action',
        'translation_domain' => 'SonataAdminBundle',
        'url' => $this->generateUrl('import'),
        'icon' => 'level-up-alt',
    ];

    return $actions;
}
这项工作,包括代码示例,根据 Creative Commons BY-SA 3.0 许可获得许可。
目录
    版本