跳到内容

操作

编辑此页

操作 是您可以在 CRUD 页面上执行的每个任务。例如,在 index 页面中,您可以执行“编辑”和“删除”列表中显示的每个实体的任务,以及执行“创建”新实体的另一个任务。

操作在您的 仪表盘CRUD 控制器configureActions() 方法中配置。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
namespace App\Controller\Admin;

use EasyCorp\Bundle\EasyAdminBundle\Config\Actions;
use EasyCorp\Bundle\EasyAdminBundle\Controller\AbstractCrudController;

class ProductCrudController extends AbstractCrudController
{
    // ...

    public function configureActions(Actions $actions): Actions
    {
        // ...
    }
}

操作名称和常量

某些方法需要一些操作的名称作为参数。除了带有操作名称的纯字符串 (如 'index', 'detail', 'edit' 等) 外,您还可以使用这些值的常量:Action::INDEX, Action::DETAIL, Action::EDIT 等 (它们在 EasyCorp\Bundle\EasyAdminBundle\Config\Action 类中定义)。

内置操作

这些是每个页面默认包含的内置操作

  • 页面 Crud::PAGE_INDEX ('index')

    • 默认添加:Action::EDIT, Action::DELETE, Action::NEW
    • 其他可用操作:Action::DETAIL
  • 页面 Crud::PAGE_DETAIL ('detail')

    • 默认添加:Action::EDIT, Action::DELETE, Action::INDEX
    • 其他可用操作:-
  • 页面 Crud::PAGE_EDIT ('edit')

    • 默认添加:Action::SAVE_AND_RETURN, Action::SAVE_AND_CONTINUE
    • 其他可用操作:Action::DELETE, Action::DETAIL, Action::INDEX
  • 页面 Crud::PAGE_NEW ('new')

    • 默认添加:Action::SAVE_AND_RETURN, Action::SAVE_AND_ADD_ANOTHER
    • 其他可用操作:Action::SAVE_AND_CONTINUE, Action::INDEX

添加操作

使用 add() 方法添加任何内置操作和你自己的自定义操作 (这将在本文后面解释)

1
2
3
4
5
6
7
8
9
10
11
12
use EasyCorp\Bundle\EasyAdminBundle\Config\Action;
use EasyCorp\Bundle\EasyAdminBundle\Config\Actions;
use EasyCorp\Bundle\EasyAdminBundle\Config\Crud;

public function configureActions(Actions $actions): Actions
{
    return $actions
        // ...
        ->add(Crud::PAGE_INDEX, Action::DETAIL)
        ->add(Crud::PAGE_EDIT, Action::SAVE_AND_ADD_ANOTHER)
    ;
}

移除操作

移除操作会使它们在界面中不可用,因此用户无法点击按钮/链接来运行这些操作。但是,用户可以破解 URL 来运行该操作。要完全禁用操作,请使用稍后解释的 disable() 方法

1
2
3
4
5
6
7
8
9
10
11
12
use EasyCorp\Bundle\EasyAdminBundle\Config\Action;
use EasyCorp\Bundle\EasyAdminBundle\Config\Actions;
use EasyCorp\Bundle\EasyAdminBundle\Config\Crud;

public function configureActions(Actions $actions): Actions
{
    return $actions
        // ...
        ->remove(Crud::PAGE_INDEX, Action::NEW)
        ->remove(Crud::PAGE_DETAIL, Action::EDIT)
    ;
}

更新操作

这主要用于更改内置操作 (例如,更改其图标、更新或删除其标签等)。 update() 方法需要一个可调用对象作为参数,EasyAdmin 会自动将其传递给操作。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
use EasyCorp\Bundle\EasyAdminBundle\Config\Action;
use EasyCorp\Bundle\EasyAdminBundle\Config\Actions;
use EasyCorp\Bundle\EasyAdminBundle\Config\Crud;

public function configureActions(Actions $actions): Actions
{
    return $actions
        // ...
        ->update(Crud::PAGE_INDEX, Action::NEW, function (Action $action) {
            return $action->setIcon('fa fa-file-alt')->setLabel(false);
        })

        // in PHP 7.4 and newer you can use arrow functions
        // ->update(Crud::PAGE_INDEX, Action::NEW,
        //     fn (Action $action) => $action->setIcon('fa fa-file-alt')->setLabel(false))
    ;
}

有条件地显示操作

某些操作必须仅在满足某些条件时才显示。例如,“查看发票”操作可能仅在订单状态为“已付款”时显示。使用 displayIf() 方法来配置何时操作应该对用户可见。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
use EasyCorp\Bundle\EasyAdminBundle\Config\Action;
use EasyCorp\Bundle\EasyAdminBundle\Config\Actions;
use EasyCorp\Bundle\EasyAdminBundle\Config\Crud;

public function configureActions(Actions $actions): Actions
{
    $viewInvoice = Action::new('View Invoice', 'fas fa-file-invoice')
        ->displayIf(static function ($entity) {
            return $entity->isPaid();
        });

        // in PHP 7.4 and newer you can use arrow functions
        // ->displayIf(fn ($entity) => $entity->isPaid())

    return $actions
        // ...
        ->add(Crud::PAGE_INDEX, $viewInvoice);
}

禁用操作

禁用操作意味着它不会显示在界面中,即使他们破解 URL,用户也无法运行该操作。如果他们尝试这样做,他们将看到“禁止操作”异常。

操作是全局禁用的,您不能按页面禁用它们

1
2
3
4
5
6
7
8
9
10
11
use EasyCorp\Bundle\EasyAdminBundle\Config\Action;
use EasyCorp\Bundle\EasyAdminBundle\Config\Actions;

public function configureActions(Actions $actions): Actions
{
    return $actions
        // ...
        // this will forbid to create or delete entities in the backend
        ->disable(Action::NEW, Action::DELETE)
    ;
}

限制操作

除了禁用操作之外,您还可以限制某些用户执行它们。使用 setPermission() 来定义查看和运行某些操作所需的 Symfony 安全权限。

权限是全局定义的;您不能为每个页面定义不同的权限

1
2
3
4
5
6
7
8
9
10
11
use EasyCorp\Bundle\EasyAdminBundle\Config\Action;
use EasyCorp\Bundle\EasyAdminBundle\Config\Actions;

public function configureActions(Actions $actions): Actions
{
    return $actions
        // ...
        ->setPermission(Action::NEW, 'ROLE_ADMIN')
        ->setPermission(Action::DELETE, 'ROLE_SUPER_ADMIN')
    ;
}

重新排序操作

使用 reorder() 来定义操作在某些页面中显示的顺序

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
use EasyCorp\Bundle\EasyAdminBundle\Config\Action;
use EasyCorp\Bundle\EasyAdminBundle\Config\Actions;
use EasyCorp\Bundle\EasyAdminBundle\Config\Crud;

public function configureActions(Actions $actions): Actions
{
    return $actions
        // ...

        // you can reorder built-in actions...
        ->reorder(Crud::PAGE_INDEX, [Action::DETAIL, Action::DELETE, Action::EDIT])

        // ...and your own custom actions too
        ->reorder(Crud::PAGE_INDEX, [Action::DETAIL, 'viewInvoice', Action::DELETE, Action::EDIT])

        // you can pass only a few actions to this method and the rest of actions
        // will be appended in their original order. In the following example, the
        // DELETE and EDIT actions are missing but they will be added automatically
        // after DETAIL and 'viewInvoice' actions
        ->reorder(Crud::PAGE_INDEX, [Action::DETAIL, 'viewInvoice'])
    ;
}

index 页面中,实体操作 (edit, delete 等) 默认在下拉菜单中显示。这样做是为了更好地显示每行的字段内容。如果您希望内联显示所有操作 (即,没有下拉菜单),请使用 showEntityActionsInlined() 方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
namespace App\Controller\Admin;

use EasyCorp\Bundle\EasyAdminBundle\Config\Crud;
use EasyCorp\Bundle\EasyAdminBundle\Controller\AbstractCrudController;

class ProductCrudController extends AbstractCrudController
{
    // ...

    public function configureCrud(Crud $crud): Crud
    {
        return $crud
            // ...
            ->showEntityActionsInlined()
        ;
    }
}

添加自定义操作

除了 EasyAdmin 提供的内置操作外,您还可以创建自己的操作。首先,使用 Action 类构造函数定义操作的基础知识 (名称、标签、图标)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// the only mandatory argument is the internal name of the action (which is
// used to add the action to some pages, to reorder the action position, etc.)
$viewInvoice = Action::new('viewInvoice');

// the second optional argument is the label visible to end users
$viewInvoice = Action::new('viewInvoice', 'Invoice');
// not defining the label explicitly or setting it to NULL means
// that the label is autogenerated from the name (e.g. 'viewInvoice' -> 'View Invoice')
$viewInvoice = Action::new('viewInvoice', null);
// set the label to FALSE to not display any label for this action (but make sure
// to display an icon for the action; otherwise users won't see it)
$viewInvoice = Action::new('viewInvoice', false);

// the third optional argument is the full CSS class of a FontAwesome icon
// see https://fontawesome.com/v5.15/icons?d=gallery&p=2&m=free
$viewInvoice = Action::new('viewInvoice', 'Invoice', 'fa fa-file-invoice');

然后,您可以配置将代表该操作的按钮/元素的基本 HTML/CSS 属性。

1
2
3
4
5
6
7
8
9
10
11
12
13
$viewInvoice = Action::new('viewInvoice', 'Invoice', 'fa fa-file-invoice')
    // renders the action as a <a> HTML element
    ->displayAsLink()
    // renders the action as a <button> HTML element
    ->displayAsButton()
    // a key-value array of attributes to add to the HTML element
    ->setHtmlAttributes(['data-foo' => 'bar', 'target' => '_blank'])
    // removes all existing CSS classes of the action and sets
    // the given value as the CSS class of the HTML element
    ->setCssClass('btn btn-primary action-foo')
    // adds the given value to the existing CSS classes of the action (this is
    // useful when customizing a built-in action, which already has CSS classes)
    ->addCssClass('some-custom-css-class text-danger')

注意

当使用 setCssClass()addCssClass() 方法时,操作将丢失 EasyAdmin 应用的默认 CSS 类 (.btn.action-<the-action-name>)。您可能需要手动添加这些 CSS 类,以使您的操作看起来符合预期。

配置完基础知识后,使用以下方法之一来定义单击操作时执行的方法

  • linkToCrudAction(): 执行当前 CRUD 控制器的某些方法;
  • linkToRoute(): 通过其路由执行某些常规 Symfony 控制器;
  • linkToUrl(): 访问外部 URL (当您的操作不是由您的应用程序提供服务时很有用)。

以下示例展示了所有类型的实际操作

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
namespace App\Controller\Admin;

use App\Entity\Invoice;
use App\Entity\Order;
use EasyCorp\Bundle\EasyAdminBundle\Config\Action;
use EasyCorp\Bundle\EasyAdminBundle\Config\Actions;
use EasyCorp\Bundle\EasyAdminBundle\Controller\AbstractCrudController;

class OrderCrudController extends AbstractCrudController
{
    // ...

    public function configureActions(Actions $actions): Actions
    {
        // this action executes the 'renderInvoice()' method of the current CRUD controller
        $viewInvoice = Action::new('viewInvoice', 'Invoice', 'fa fa-file-invoice')
            ->linkToCrudAction('renderInvoice');

        // if the method is not defined in a CRUD controller, link to its route
        $sendInvoice = Action::new('sendInvoice', 'Send invoice', 'fa fa-envelope')
            // if the route needs parameters, you can define them:
            // 1) using an array
            ->linkToRoute('invoice_send', [
                'send_at' => (new \DateTime('+ 10 minutes'))->format('YmdHis'),
            ])

            // 2) using a callable (useful if parameters depend on the entity instance)
            // (the type-hint of the function argument is optional but useful)
            ->linkToRoute('invoice_send', function (Order $order): array {
                return [
                    'uuid' => $order->getId(),
                    'method' => $order->getUser()->getPreferredSendingMethod(),
                ];
            });

        // this action points to the invoice on Stripe application
        $viewStripeInvoice = Action::new('viewInvoice', 'Invoice', 'fa fa-file-invoice')
            ->linkToUrl(function (Order $entity) {
                return 'https://www.stripe.com/invoice/'.$entity->getStripeReference();
            });

        return $actions
            // ...
            ->add(Crud::PAGE_DETAIL, $viewInvoice)
            ->add(Crud::PAGE_DETAIL, $sendInvoice)
            ->add(Crud::PAGE_DETAIL, $viewStripeInvoice)
        ;
    }

    public function renderInvoice(AdminContext $context)
    {
        $order = $context->getEntity()->getInstance();

        // add your logic here...
    }
}

批量操作

批量操作是一种特殊类型的操作,它同时应用于多个项目。它们仅在 index 页面中可用。

假设您使用 User 实体管理用户,并且常见的任务是批准他们的注册。与其像前面几节中解释的那样创建普通的 approve 操作,不如创建一个批量操作以提高效率并一次批准多个用户。

首先,使用 addBatchAction() 方法将其添加到您的操作配置中

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
namespace App\Controller\Admin;

use EasyCorp\Bundle\EasyAdminBundle\Config\Action;
use EasyCorp\Bundle\EasyAdminBundle\Config\Actions;
use EasyCorp\Bundle\EasyAdminBundle\Controller\AbstractCrudController;

class UserCrudController extends AbstractCrudController
{
    // ...

    public function configureActions(Actions $actions): Actions
    {
        return $actions
            // ...
            ->addBatchAction(Action::new('approve', 'Approve Users')
                ->linkToCrudAction('approveUsers')
                ->addCssClass('btn btn-primary')
                ->setIcon('fa fa-user-check'))
        ;
    }
}

批量操作支持与其他操作相同的配置选项,并且它们可以链接到 CRUD 控制器方法、Symfony 路由或某些 URL。如果至少有一个批量操作,则后端界面将更新以添加一些“复选框”,这些复选框允许选择索引列表中的多行。

当用户单击批量操作链接/按钮时,将使用 POST 方法将表单提交到操作中配置的操作或路由。获取提交数据的最简单方法是使用 EasyCorp\Bundle\EasyAdminBundle\Dto\BatchActionDto 类类型提示您的批量操作方法的某些参数。如果您这样做,EasyAdmin 将注入一个包含所有批量操作数据的 DTO

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
namespace App\Controller\Admin;

use EasyCorp\Bundle\EasyAdminBundle\Context\AdminContext;
use EasyCorp\Bundle\EasyAdminBundle\Controller\AbstractCrudController;
use EasyCorp\Bundle\EasyAdminBundle\Dto\BatchActionDto;

class UserCrudController extends AbstractCrudController
{
    // ...

    public function approveUsers(BatchActionDto $batchActionDto)
    {
        $entityManager = $this->getDoctrine()->getManagerForClass($batchActionDto->getEntityFqcn());
        foreach ($batchActionDto->getEntityIds() as $id) {
            $user = $entityManager->find($id);
            $user->approve();
        }

        $entityManager->flush();

        return $this->redirect($batchActionDto->getReferrerUrl());
    }
}

注意

作为替代方案,除了注入 BatchActionDto 变量外,您还可以注入 Symfony 的 Request 对象以获取所有原始提交的批量数据 (例如 $request->request->get('batchActionEntityIds'))。

集成 Symfony 操作

如果操作逻辑很小并且直接与后端相关,则可以将其添加到 CRUD 控制器,因为这大大简化了其在 EasyAdmin 中的集成。但是,有时您的某些逻辑过于复杂或在 Symfony 应用程序的其他部分中使用,因此您无法将其移动到 CRUD 控制器。本节介绍如何将现有的 Symfony 操作集成到 EasyAdmin 中,以便您可以重用后端布局、菜单和其他功能。

假设您的 Symfony 应用程序具有计算有关您的客户的一些业务统计信息 (平均订单金额、年度购买次数等) 的操作。所有这些都在 BusinessStatsCalculator 服务中计算,因此您无法创建 CRUD 控制器来显示该信息。相反,创建一个名为 BusinessStatsController 的普通 Symfony 控制器

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/Admin/BusinessStatsController.php
namespace App\Controller\Admin;

use App\Stats\BusinessStatsCalculator;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Security;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\Routing\Annotation\Route;

/**
 * @Security("is_granted('ROLE_ADMIN')")
 */
class BusinessStatsController extends AbstractController
{
    public function __construct(BusinessStatsCalculator $businessStatsCalculator)
    {
        $this->businessStatsCalculator = $businessStatsCalculator;
    }

    /**
     * @Route("/admin/business-stats", name="admin_business_stats")
     */
    public function index()
    {
        return $this->render('admin/business_stats/index.html.twig', [
            'data' => $this->businessStatsCalculator->getStatsSummary(),
        ]);
    }

    /**
     * @Route("/admin/business-stats/{id}", name="admin_business_stats_customer")
     */
    public function customer(Customer $customer)
    {
        return $this->render('admin/business_stats/customer.html.twig', [
            'data' => $this->businessStatsCalculator->getCustomerStats($customer),
        ]);
    }
}

这是一个普通的 Symfony 控制器 (它不扩展任何 EasyAdmin 类),具有一些逻辑,可在 Twig 模板中呈现结果 (稍后将显示)。将此集成到您的 EasyAdmin 后端的第一个步骤是使用 configureMenuItems() 方法将其添加到主菜单。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// src/Controller/Admin/DashboardController.php
namespace App\Controller\Admin;

use EasyCorp\Bundle\EasyAdminBundle\Config\Dashboard;
use EasyCorp\Bundle\EasyAdminBundle\Controller\AbstractDashboardController;

class DashboardController extends AbstractDashboardController
{
    // ...

    public function configureMenuItems(): iterable
    {
        // ...

        yield MenuItem::linktoRoute('Stats', 'fa fa-chart-bar', 'admin_business_stats');
    }
}

如果您重新加载后端并单击该新菜单项,您将看到一个错误,因为 BusinessStatsController 使用的模板尚未创建。查看页面的 URL,您将看到 EasyAdmin 用于集成 Symfony 操作的技巧。

生成的 URL 不是预期的 /admin/business-stats 简洁 URL,而是 /admin?menuIndex=...&submenuIndex=...&routeName=admin_business_stats。这是一个管理 URL,因此 EasyAdmin 可以创建 管理上下文、加载相应的菜单等。但是,由于 routeName 查询字符串参数,EasyAdmin 知道它必须将请求转发到服务该路由的 Symfony 控制器,并且透明地为您执行此操作。

注意

以这种方式处理路由参数在大多数情况下都很好。但是,有时您需要将路由参数作为适当的 Symfony 路由参数处理。例如,如果您想为 Symfony 的模拟功能传递 _switch_user 查询参数,您可以这样做

1
2
3
4
5
6
7
8
9
// you can generate the full URL with Symfony's URL generator:
$impersonate = Action::new('impersonate')->linkToUrl(
    $urlGenerator->generate('admin', ['_switch_user' => 'user@example.com'], UrlGeneratorInterface::ABSOLUTE_URL)
);

// or you can add the query string parameter directly:
$impersonate = Action::new('impersonate')
    ->linkToRoute('some_route')
    ->setQueryParameter('_switch_user', 'user@example.com');

现在,创建 index() 方法使用的模板,该模板列出了所有客户的统计信息摘要,并包含指向每个客户详细统计信息的链接。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
{# templates/admin/business_stats/index.html.twig #}
{% extends '@EasyAdmin/page/content.html.twig' %}

{% block page_title 'Business Stats' %}
{% block page_content %}
    <table>
        <thead> {# ... #} </thead>
        <tbody>
            {% for customer_data in data %}
                <tr>
                    {# ... #}

                    <td>
                        <a href="{{ ea_url().setRoute('admin_business_stats_customer', { id: customer_data.id }) }}">
                            View Details
                        </a>
                    </td>
                </tr>
            {% endfor %}
        </tbody>
    </table>
{% endblock %}

Twig 模板扩展了 EasyAdmin 提供的 内容页面模板,以重用所有后端设计。模板的其余部分是普通的 Twig 代码,除了 URL 生成。您必须使用 ea_url() 函数 并传递 Symfony 路由名称和参数,而不是使用 Symfony 的 path() 函数。

与之前发生的情况类似,生成的 URL 不是预期的 /admin/business-stats/5,而是 /admin?routeName=admin_business_stats_customer&routeParams%5Bid%5D=5。但这很好。EasyAdmin 将运行您的 BusinessStatsController 的 customer() 方法,因此您可以呈现另一个包含客户统计信息的 Twig 模板。

为集成在 EasyAdmin 中的 Symfony 操作生成 URL

如上一节详细解释的那样,在将 Symfony 操作集成到 EasyAdmin 后端时,您需要以稍微不同的方式生成 URL。您必须使用 EasyAdmin 提供的 AdminUrlGenerator 服务,而不是使用 Symfony 的 UrlGenerator 服务或控制器中的 $this->generateUrl() 快捷方式。

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
// src/Controller/SomeController.php
namespace App\Controller;

use EasyCorp\Bundle\EasyAdminBundle\Router\AdminUrlGenerator;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\Routing\Annotation\Route;

class SomeController extends AbstractController
{
    private $adminUrlGenerator;

    public function __construct(AdminUrlGenerator $adminUrlGenerator)
    {
        $this->adminUrlGenerator = $adminUrlGenerator;
    }

    public function someMethod()
    {
        $url = $this->adminUrlGenerator->setRoute('admin_business_stats_customer', [
            'id' => $this->getUser()->getId(),
        ])->generateUrl();

        // ...
    }
}
这项工作,包括代码示例,已获得 Creative Commons BY-SA 3.0 许可协议的许可。
目录
    版本