跳到内容

CRUD 控制器

编辑此页

CRUD 控制器 为 Doctrine ORM 实体提供 CRUD 操作(创建、显示、更新、删除)。每个 CRUD 控制器可以关联到一个或多个仪表盘。

从技术上讲,这些 CRUD 控制器是常规的 Symfony 控制器,因此您可以像在控制器中通常做的那样进行任何操作,例如注入服务和使用快捷方式,如 $this->render()$this->isGranted()

CRUD 控制器必须实现 EasyCorp\Bundle\EasyAdminBundle\Contracts\Controller\CrudControllerInterface 接口,这确保在控制器中定义了某些方法。除了实现接口,您还可以从 AbstractCrudController 类扩展。运行以下命令以生成 CRUD 控制器的基本结构

1
$ php bin/console make:admin:crud

CRUD 控制器页面

CRUD 控制器的四个主要页面是

  • index,显示实体列表,可以分页、按列排序,并使用搜索查询和过滤器进行优化;
  • detail,显示给定实体的内容;
  • new,允许创建新的实体实例;
  • edit,允许更新给定实体的任何属性。

这些页面是在 AbstractCrudController 控制器中使用四个同名操作生成的。此控制器定义了其他辅助操作(例如,deleteautocomplete),这些操作不匹配任何页面。

CRUD 路由

当使用 漂亮的管理员 URL 时,每个 CRUD 操作都定义了一个默认遵循此名称和路径的管理员路由

CRUD 路由名称 CRUD 路由路径
*_index /
*_new /new
*_batch_delete /batch-delete
*_autocomplete /autocomplete
*_edit /{entityId}/edit
*_delete /{entityId}/delete
*_detail /{entityId}

例如,对于名为 ProductCrudController 的 CRUD 控制器,它属于一个路由名为 admin 且路径为 /admin 的后端,它将生成以下路由

管理路由名称 管理路由路径
admin_product_index /admin/product
admin_product_new /admin/product/new
admin_product_batch_delete /admin/product/batch-delete
admin_product_autocomplete /admin/product/autocomplete
admin_product_edit /admin/product/324/edit
admin_product_delete /admin/product/324/delete
admin_product_detail /admin/product/324

提示

默认情况下,EasyAdmin 在所有仪表盘上为所有 CRUD 控制器生成路由。您可以限制每个仪表盘上可访问的控制器,以避免生成所有这些路由。

您可以使用 #[AdminDashboard] 属性的 routes 选项自定义某些仪表盘提供的所有 CRUD 控制器的操作的路由名称和/或路径

1
2
3
4
5
6
7
8
9
10
11
12
13
14
use EasyCorp\Bundle\EasyAdminBundle\Attribute\AdminDashboard;
// ...

#[AdminDashboard(routePath: '/admin', routeName: 'admin', routes: [
    'index' => ['routePath' => '/all'],
    'new' => ['routePath' => '/create', 'routeName' => 'create'],
    'edit' => ['routePath' => '/editing-{entityId}', 'routeName' => 'editing'],
    'delete' => ['routePath' => '/remove/{entityId}'],
    'detail' => ['routeName' => 'view'],
])]
class SomeDashboardController extends AbstractDashboardController
{
    // ...
}

使用此配置,ProductCrudController 操作的路由将是

管理路由名称 管理路由路径
admin_product_index /admin/product/all
admin_product_create /admin/product/create
admin_product_batch_delete /admin/product/current/batch-delete
admin_product_autocomplete /admin/product/current/autocomplete
admin_product_editing /admin/product/current/editing-324
admin_product_delete /admin/product/remove/324
admin_product_view /admin/product/324

您还可以使用带有以下选项的 #[AdminCrud] 属性自定义 CRUD 控制器的路径和/或路由名称

  • routePath:表示整个路由路径中控制器的值(例如,/foo 路径将导致 /admin + /foo + /<action>);
  • routeName:表示完整路由名称中控制器的值(例如,foo_bar 路由名称将导致 admin_ + foo_bar + _<action>)。

使用与上面相同的示例,您可以按如下方式配置控制器的路由名称和路径

1
2
3
4
5
6
7
8
use EasyCorp\Bundle\EasyAdminBundle\Attribute\AdminCrud;
// ...

#[AdminCrud(routePath: '/stock/current', routeName: 'stock')]
class ProductCrudController extends AbstractCrudController
{
    // ...
}

路由名称/路径将不再是 admin_product_*/admin/product/*,而是以下内容

管理路由名称 管理路由路径
admin_stock_index /admin/stock/current
admin_stock_new /admin/stock/current/new
admin_stock_batch_delete /admin/stock/current/batch-delete
admin_stock_autocomplete /admin/stock/current/autocomplete
admin_stock_edit /admin/stock/current/324/edit
admin_stock_delete /admin/stock/current/324/delete
admin_stock_detail /admin/stock/current/324

最后,您还可以使用 #[AdminAction] 属性自定义每个 CRUD 控制器操作的路由名称和/或路径

1
2
3
4
5
6
7
8
9
10
11
12
13
use EasyCorp\Bundle\EasyAdminBundle\Attribute\AdminAction;
// ...

class ProductCrudController extends AbstractCrudController
{
    // ...

    #[AdminAction(routePath: '/latest-products', routeName: 'latest')]
    public function index(AdminContext $context)
    {
        // ...
    }
}

此控制器的 index() 操作将不再使用 admin_product_index 路由名称和 URL 中的 /admin/product 路径。相反,路由名称将是 admin_product_latest,路径将是 /admin/product/latest-products

提示

您可以组合 #[AdminDashboard]#[AdminCrud]#[AdminAction] 属性来自定义部分或全部路由名称和路径。

页面名称和常量

某些方法需要一些 CRUD 页面的名称作为参数。您可以使用以下任何字符串:'index''detail''edit''new'。如果您喜欢使用常量来表示这些值,请使用 Crud::PAGE_INDEXCrud::PAGE_DETAILCrud::PAGE_EDITCrud::PAGE_NEW(它们在 EasyCorp\Bundle\EasyAdminBundle\Config\Crud 类中定义)。

CRUD 控制器配置

CRUD 控制器的唯一强制配置选项是控制器管理的 Doctrine 实体的 FQCN。这被定义为一个公共静态方法

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

use App\Entity\Product;
use EasyCorp\Bundle\EasyAdminBundle\Controller\AbstractCrudController;

class ProductCrudController extends AbstractCrudController
{
    // it must return a FQCN (fully-qualified class name) of a Doctrine ORM entity
    public static function getEntityFqcn(): string
    {
        return Product::class;
    }

    // ...
}

其余 CRUD 选项使用 configureCrud() 方法配置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
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
            ->setEntityLabelInSingular('...')
            ->setDateFormat('...')
            // ...
        ;
    }
}

设计选项

1
2
3
4
5
6
7
8
9
10
11
12
public function configureCrud(Crud $crud): Crud
{
    return $crud
        // set this option if you prefer the page content to span the entire
        // browser width, instead of the default design which sets a max width
        ->renderContentMaximized()

        // set this option if you prefer the sidebar (which contains the main menu)
        // to be displayed as a narrow column instead of the default expanded design
        ->renderSidebarMinimized()
    ;
}

实体选项

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
public function configureCrud(Crud $crud): Crud
{
    return $crud
        // the labels used to refer to this entity in titles, buttons, etc.
        ->setEntityLabelInSingular('Product')
        ->setEntityLabelInPlural('Products')

        // in addition to a string, the argument of the singular and plural label methods
        // can be a closure that defines two nullable arguments: entityInstance (which will
        // be null in 'index' and 'new' pages) and the current page name
        ->setEntityLabelInSingular(
            fn (?Product $product, ?string $pageName) => $product ? $product->toString() : 'Product'
        )
        ->setEntityLabelInPlural(function (?Category $category, ?string $pageName) {
            return 'edit' === $pageName ? $category->getLabel() : 'Categories';
        })

        // the Symfony Security permission needed to manage the entity
        // (none by default, so you can manage all instances of the entity)
        ->setEntityPermission('ROLE_EDITOR')
    ;
}

标题和帮助选项

默认情况下,indexnew 页面的页面标题基于使用 setEntityLabelInSingular()setEntityLabelInPlural() 方法定义的 实体选项 值。在 detailedit 页面中,EasyAdmin 首先尝试将实体转换为字符串表示形式,否则回退到通用标题。

您可以使用以下方法覆盖默认页面标题

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
public function configureCrud(Crud $crud): Crud
{
    return $crud
        // the visible title at the top of the page and the content of the <title> element
        // it can include these placeholders:
        //   %entity_name%, %entity_as_string%,
        //   %entity_id%, %entity_short_id%
        //   %entity_label_singular%, %entity_label_plural%
        ->setPageTitle('index', '%entity_label_plural% listing')

        // you can pass a PHP closure as the value of the title
        ->setPageTitle('new', fn () => new \DateTime('now') > new \DateTime('today 13:00') ? 'New dinner' : 'New lunch')

        // in DETAIL and EDIT pages, the closure receives the current entity
        // as the first argument
        ->setPageTitle('detail', fn (Product $product) => (string) $product)
        ->setPageTitle('edit', fn (Category $category) => sprintf('Editing <b>%s</b>', $category->getName()))

        // the help message displayed to end users (it can contain HTML tags)
        ->setHelp('edit', '...')
    ;
}

EasyAdmin 将 raw 过滤器应用于模板中显示的所有标题、标签、帮助消息等。这样做是为了允许您使用 HTML 标签自定义所有内容(因为这些标签将被呈现而不是转义)。

这就是 EasyAdmin 使用的默认页面标题仅包含安全内容(如实体名称和 ID)的原因。否则,您的后端可能会受到 XSS 攻击

如果您更改默认页面标题以包含占位符 %entity_as_string%,请检查您是否未将用户创建的内容包含在相关实体的 __toString() 方法返回的值中。如果您无法避免这种情况,请确保使用 Symfony HtmlSanitizer 组件 对任何用户提交的数据进行清理。

日期、时间和数字格式化选项

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
public function configureCrud(Crud $crud): Crud
{
    return $crud
        // the argument must be either one of these strings: 'short', 'medium', 'long', 'full', 'none'
        // (the strings are also available as \EasyCorp\Bundle\EasyAdminBundle\Field\DateTimeField::FORMAT_* constants)
        // or a valid ICU Datetime Pattern (see https://unicode-org.github.io/icu/userguide/format_parse/datetime/)
        ->setDateFormat('...')
        ->setTimeFormat('...')

        // first argument = datetime pattern or date format; second optional argument = time format
        ->setDateTimeFormat('...', '...')

        ->setDateIntervalFormat('%%y Year(s) %%m Month(s) %%d Day(s)')
        ->setTimezone('...')

        // this option makes numeric values to be rendered with a sprintf()
        // call using this value as the first argument.
        // this option overrides any formatting option for all numeric values
        // (e.g. setNumDecimals(), setRoundingMode(), etc. are ignored)
        // NumberField and IntegerField can override this value with their
        // own setNumberFormat() methods, which works in the same way
        ->setNumberFormat('%.2d')

        // Sets the character used to separate each thousand group in a number
        // e.g. if separator is ',' then 12345 is formatted as 12,345
        // By default, EasyAdmin doesn't add any thousands separator to numbers;
        // NumberField and IntegerField can override this value with their
        // own setThousandsSeparator() methods, which works in the same way
        ->setThousandsSeparator(',')

        // Sets the character used to separate the decimal part of a non-integer number
        // e.g. if separator is '.' then 1/10 is formatted as 0.1
        // by default, EasyAdmin displays the default decimal separator used by PHP;
        // NumberField and IntegerField can override this value with their
        // own setDecimalSeparator() methods, which works in the same way
        ->setDecimalSeparator('.')
    ;
}

搜索、排序和分页选项

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
public function configureCrud(Crud $crud): Crud
{
    return $crud
        // ...

        // the names of the Doctrine entity properties where the search is made on
        // (by default it looks for in all properties)
        ->setSearchFields(['name', 'description'])
        // use dots (e.g. 'seller.email') to search in Doctrine associations
        ->setSearchFields(['name', 'description', 'seller.email', 'seller.address.zipCode'])
        // set it to null to disable and hide the search box
        ->setSearchFields(null)
        // call this method to focus the search input automatically when loading the 'index' page
        ->setAutofocusSearch()

        // by default, the search results match all the terms (SearchMode::ALL_TERMS):
        // term1 in (field1 or field2) AND term2 in (field1 or field2)
        // e.g. if you look for 'lorem ipsum' in [title, description],
        // results require matching 'lorem' in either title or description
        // (or both) AND 'ipsum' in either title or description (or both)
        ->setSearchMode(SearchMode::ALL_TERMS)

        // use the SearchMode::ANY_TERMS option to change the search mode to
        // match at least one of the terms:
        // term1 in (field1 or field2) OR term2 in (field1 or field2)
        // e.g. if you look for 'lorem ipsum' in [title, description],
        // results will match either 'lorem' in title or description (or both)
        // OR 'ipsum' in title or description (or both)
        ->setSearchMode(SearchMode::ANY_TERMS)
    ;
}

提示

默认情况下,搜索引擎拆分所有术语(搜索 foo bar 返回包含 foobar 的项目)。您可以使用引号将全部或部分查询括起来以进行精确搜索:"foo bar" 仅返回包含该确切内容的项目,包括中间的空格。

1
2
3
4
5
6
7
8
9
10
11
12
13
public function configureCrud(Crud $crud): Crud
{
    return $crud
        // ...

        // defines the initial sorting applied to the list of entities
        // (user can later change this sorting by clicking on the table columns)
        ->setDefaultSort(['id' => 'DESC'])
        ->setDefaultSort(['id' => 'DESC', 'title' => 'ASC', 'startsAt' => 'DESC'])
        // you can sort by Doctrine associations up to two levels
        ->setDefaultSort(['seller.name' => 'ASC'])
    ;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
public function configureCrud(Crud $crud): Crud
{
    return $crud
        // ...

        // the max number of entities to display per page
        ->setPaginatorPageSize(30)
        // the number of pages to display on each side of the current page
        // e.g. if num pages = 35, current page = 7 and you set ->setPaginatorRangeSize(4)
        // the paginator displays: [Previous]  1 ... 3  4  5  6  [7]  8  9  10  11 ... 35  [Next]
        // set this number to 0 to display a simple "< Previous | Next >" pager
        ->setPaginatorRangeSize(4)

        // these are advanced options related to Doctrine Pagination
        // (see https://www.doctrine-project.org/projects/doctrine-orm/en/2.7/tutorials/pagination.html)
        ->setPaginatorUseOutputWalkers(true)
        ->setPaginatorFetchJoinCollection(true)
    ;
}

注意

当使用 Doctrine 过滤器 时,列表可能不包含某些项目,因为这些项目已被这些全局 Doctrine 过滤器删除。当请求 URL 属于仪表盘时,使用仪表盘路由名称以不应用过滤器。您还可以通过 应用程序上下文变量 获取仪表盘路由名称。

默认情况下,执行 Doctrine 查询以获取 index 页面中显示的实体列表时,会考虑排序配置、可选的搜索查询、可选的 过滤器 和分页。如果您需要完全自定义此查询,请在您的 CRUD 控制器中覆盖 createIndexQueryBuilder() 方法。

模板和表单选项

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
public function configureCrud(Crud $crud): Crud
{
    return $crud
        // this method allows to use your own template to render a certain part
        // of the backend instead of using EasyAdmin default template
        // the first argument is the "template name", which is the same as the
        // Twig path but without the `@EasyAdmin/` prefix and the `.html.twig` suffix
        ->overrideTemplate('crud/field/id', 'admin/fields/my_id.html.twig')

        // the theme/themes to use when rendering the forms of this entity
        // (in addition to EasyAdmin default theme)
        ->addFormTheme('foo.html.twig')
        // this method overrides all existing form themes (including the
        // default EasyAdmin form theme)
        ->setFormThemes(['my_theme.html.twig', 'admin.html.twig'])

        // this sets the options of the entire form (later, you can set the options
        // of each form type via the methods of their associated fields)
        // pass a single array argument to apply the same options for the new and edit forms
        ->setFormOptions([
            'validation_groups' => ['Default', 'my_validation_group']
        ]);

        // pass two array arguments to apply different options for the new and edit forms
        // (pass an empty array argument if you want to apply no options to some form)
        ->setFormOptions(
            ['validation_groups' => ['my_validation_group']],
            ['validation_groups' => ['Default'], '...' => '...'],
        );
    ;
}

其他选项

1
2
3
4
5
6
7
8
9
10
11
public function configureCrud(Crud $crud): Crud
{
    return $crud
        // by default, when the value of some field is `null`, EasyAdmin displays
        // a label with the `null` text. You can change that by overriding
        // the `label/null` template. However, if you have lots of `null` values
        // and want to reduce the "visual noise" in your backend, you can use
        // the following option to not display anything when some value is `null`
        // (this option is applied both in the `index` and `detail` pages)
        ->hideNullValues()
}

创建或编辑实体后自定义重定向

默认情况下,在创建或编辑实体时单击“保存”按钮后,您将被重定向到上一页。如果您想更改此行为,请覆盖 getRedirectResponseAfterSave() 方法。

例如,如果您添加了一个名为“保存并查看详情”的 自定义操作,您可能更喜欢在保存更改后重定向到详情页面

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
protected function getRedirectResponseAfterSave(AdminContext $context, string $action): RedirectResponse
{
    $submitButtonName = $context->getRequest()->request->all()['ea']['newForm']['btn'];

    if ('saveAndViewDetail' === $submitButtonName) {
        // when using pretty admin URLs
        return $this->redirectToRoute('admin_product_detail', [
            'entityId' => $context->getEntity()->getPrimaryKeyValue(),
        ]);

        // when using legacy admin URLs
        $url = $this->container->get(AdminUrlGenerator::class)
            ->setAction(Action::DETAIL)
            ->setEntityId($context->getEntity()->getPrimaryKeyValue())
            ->generateUrl();

        return $this->redirect($url);
    }

    return parent::getRedirectResponseAfterSave($context, $action);
}

不同 CRUD 控制器中的相同配置

如果您想在所有 CRUD 控制器中执行相同的配置,则无需在每个控制器中重复配置。相反,在您的仪表盘中添加 configureCrud() 方法,所有控制器都将继承该配置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
use EasyCorp\Bundle\EasyAdminBundle\Attribute\AdminDashboard;
use EasyCorp\Bundle\EasyAdminBundle\Config\Crud;
use EasyCorp\Bundle\EasyAdminBundle\Config\Dashboard;
use EasyCorp\Bundle\EasyAdminBundle\Controller\AbstractDashboardController;

#[AdminDashboard(routePath: '/admin', routeName: 'admin')]
class DashboardController extends AbstractDashboardController
{
    // ...

    public function configureCrud(): Crud
    {
        return Crud::new()
            // this defines the pagination size for all CRUD controllers
            // (each CRUD controller can override this value if needed)
            ->setPaginatorPageSize(30)
        ;
    }
}

字段

字段允许在每个CRUD 页面上显示 Doctrine 实体的内容。EasyAdmin 提供了内置字段来显示所有常见数据类型,但您也可以创建自己的字段

如果您的 CRUD 控制器从 AbstractCrudController 扩展,则字段会自动配置。在 index 页面中,您将看到一些字段,在其余页面中,您将看到尽可能多的字段以显示 Doctrine 实体的所有属性。

阅读关于字段的章节,了解如何配置要在每个页面上显示的字段,如何配置每个字段的呈现方式等等。

自定义 CRUD 操作

默认的 CRUD 操作(控制器中的 index()detail()edit()new()delete() 方法)实现了应用程序中最常用的行为。

自定义其行为的第一种方法是在您自己的控制器中覆盖这些方法。但是,原始操作非常通用,以至于它们包含相当多的代码,因此覆盖它们不是很方便。

相反,您可以覆盖其他较小的方法,这些方法实现了 CRUD 操作所需的某些功能。例如,index() 操作调用名为 createIndexQueryBuilder() 的方法来创建用于获取索引列表中显示的结果的 Doctrine 查询构建器。如果您想自定义该列表,最好覆盖 createIndexQueryBuilder() 方法,而不是整个 index() 方法。有很多这样的方法,因此您应该检查 EasyCorp\Bundle\EasyAdminBundle\Controller\AbstractCrudController 类。

自定义 CRUD 操作的另一种选择是使用 EasyAdmin 触发的事件,例如 BeforeCrudActionEventAfterCrudActionEvent

创建、持久化和删除实体

CRUD 控制器的大多数操作最终会创建、持久化或删除实体。如果您的 CRUD 控制器从 AbstractCrudController 扩展,则这些方法已经实现,但您可以通过覆盖方法和监听事件来自定义它们。

首先,您可以覆盖 createEntity()updateEntity()persistEntity()deleteEntity() 方法。createEntity() 方法例如仅执行 return new $entityFqcn(),因此如果您的实体需要传递构造函数参数或设置其某些属性,则需要覆盖它

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

use App\Entity\Product;
use EasyCorp\Bundle\EasyAdminBundle\Controller\AbstractCrudController;

class ProductCrudController extends AbstractCrudController
{
    public static function getEntityFqcn(): string
    {
        return Product::class;
    }

    public function createEntity(string $entityFqcn)
    {
        $product = new Product();
        $product->createdBy($this->getUser());

        return $product;
    }

    // ...
}

覆盖此行为的另一种方法是监听 EasyAdmin 触发的事件,当实体被创建、更新、持久化、删除等时。

将其他变量传递给 CRUD 模板

AbstractCrudController 中实现的默认 CRUD 操作不以通常的 $this->render('...') 指令结束,以呈现 Twig 模板并在 Symfony Response 对象中返回其内容。

相反,CRUD 操作返回一个 EasyCorp\Bundle\EasyAdminBundle\Config\KeyValueStore 对象,其中包含传递给模板的变量,该模板呈现 CRUD 操作内容。此 KeyValueStore 对象类似于 Symfony 的 ParameterBag 对象。它就像一个面向对象的数组,具有有用的方法,例如 get()set()has() 等。

在结束每个 CRUD 操作之前,它们的 KeyValueStore 对象将传递给一个名为 configureResponseParameters() 的方法,您可以在自己的控制器中覆盖该方法以添加/删除/更改这些模板变量

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

use App\Entity\Product;
use EasyCorp\Bundle\EasyAdminBundle\Config\Crud;
use EasyCorp\Bundle\EasyAdminBundle\Config\KeyValueStore;
use EasyCorp\Bundle\EasyAdminBundle\Controller\AbstractCrudController;

class ProductCrudController extends AbstractCrudController
{
    // ...

    public function configureResponseParameters(KeyValueStore $responseParameters): KeyValueStore
    {
        if (Crud::PAGE_DETAIL === $responseParameters->get('pageName')) {
            $responseParameters->set('foo', '...');

            // keys support the "dot notation", so you can get/set nested
            // values separating their parts with a dot:
            $responseParameters->setIfNotSet('bar.foo', '...');
            // this is equivalent to: $parameters['bar']['foo'] = '...'
        }

        return $responseParameters;
    }
}

您可以根据需要向此 KeyValueStore 对象添加任意数量的参数。唯一必需的参数是 templateNametemplatePath,分别用于设置要呈现为 CRUD 操作结果的模板的名称或路径。

模板名称和模板路径

EasyAdmin 用于呈现其内容的所有模板都是可配置的。这就是为什么 EasyAdmin 处理“模板名称”而不是正常的 Twig 模板路径。

模板名称与模板路径相同,但没有 @EasyAdmin 前缀和 .html.twig 后缀。例如,@EasyAdmin/layout.html.twig 指的是 EasyAdmin 提供的内置布局模板。但是,layout 指的是“配置为应用程序布局的任何模板”。

使用模板名称而不是路径使您可以完全灵活地自定义应用程序行为,同时保留所有自定义模板。在 Twig 模板中,使用 ea.templatePath() 函数获取与给定模板名称关联的 Twig 路径

1
2
3
4
5
6
7
<div id="flash-messages">
    {{ include(ea.templatePath('flash_messages')) }}
</div>

{% if some_value is null %}
    {{ include(ea.templatePath('label/null')) }}
{% endif %}

生成管理 URL

当使用 漂亮的管理员 URL 时,EasyAdmin 为每个仪表盘的每个 CRUD 操作生成一个路由。您可以使用以下命令列出所有路由

1
$ php bin/console debug:router

如果您看不到某些或任何管理员路由,请清除 Symfony 应用程序的缓存,以便 EasyAdmin 路由加载器可以再次生成它们

1
$ php bin/console cache:clear

由于 Symfony 提供的 实用程序用于生成 URL,您可以使用这些路由中的任何一个来生成管理 URL

1
2
3
4
5
6
7
8
9
10
// redirecting to an admin URL inside a controller
return $this->redirectToRoute('admin_product_new');

// generating an admin URL inside a service
$userProfileUrl = $this->router->generate('admin_user_detail', [
    'entityId' => $user->getId(),
]);

// generating an admin URL in a Twig template
<a href="{{ path('admin_blog_post_edit', {entityId: post.id}) }}">Edit Blog Post</a>

构建管理 URL

如果您不使用 漂亮的管理员 URL,或者如果您需要动态构建路由,则可以使用 EasyAdmin 提供的 AdminUrlGenerator 来构建管理 URL。

以这种方式生成 URL 时,您不是从头开始。EasyAdmin 重用当前请求中存在的所有查询参数。这样做是有目的的,因为基于当前 URL 生成新 URL 是最常见的场景。使用 unsetAll() 方法删除所有现有查询参数

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

use EasyCorp\Bundle\EasyAdminBundle\Controller\AbstractCrudController;
use EasyCorp\Bundle\EasyAdminBundle\Router\AdminUrlGenerator;

class SomeCrudController extends AbstractCrudController
{
    private $adminUrlGenerator;

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

    // ...

    public function someMethod()
    {
        // instead of injecting the AdminUrlGenerator service in the constructor,
        // you can also get it from inside a controller action as follows:
        // $adminUrlGenerator = $this->container->get(AdminUrlGenerator::class);

        // the existing query parameters are maintained, so you only
        // have to pass the values you want to change.
        $url = $this->adminUrlGenerator->set('page', 2)->generateUrl();

        // you can remove existing parameters
        $url = $this->adminUrlGenerator->unset('menuIndex')->generateUrl();
        $url = $this->adminUrlGenerator->unsetAll()->set('foo', 'someValue')->generateUrl();

        // the URL builder provides shortcuts for the most common parameters
        $url = $this->adminUrlGenerator
            ->setController(SomeCrudController::class)
            ->setAction('theActionName')
            ->generateUrl();

        // ...
    }
}

提示

如果您需要出于任何原因手动处理管理 URL,则查询字符串参数的名称在 EA 类中定义为常量。

由于 ea_url() Twig 函数,模板中可以使用完全相同的功能。在模板中,您可以省略对 generateUrl() 方法的调用(它将自动为您调用)

1
2
3
4
5
6
7
8
9
{# both are equivalent #}
{% set url = ea_url({ page: 2 }).generateUrl() %}
{% set url = ea_url({ page: 2 }) %}

{% set url = ea_url().set('page', 2) %}

{% set url = ea_url()
    .setController('App\\Controller\\Admin\\SomeCrudController')
    .setAction('theActionName') %}

从 EasyAdmin 外部生成 CRUD URL

当从 EasyAdmin 外部(例如,从常规 Symfony 控制器)生成 EasyAdmin 页面的 URL 时,管理上下文变量不可用。这就是为什么您必须始终设置与 URL 关联的 CRUD 控制器。如果您有多个仪表盘,则还必须设置仪表盘

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
use App\Controller\Admin\DashboardController;
use App\Controller\Admin\ProductCrudController;
use EasyCorp\Bundle\EasyAdminBundle\Config\Action;
use EasyCorp\Bundle\EasyAdminBundle\Router\AdminUrlGenerator;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;

class SomeSymfonyController extends AbstractController
{
    private $adminUrlGenerator;

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

    public function someMethod()
    {
        // if your application only contains one Dashboard, it's enough
        // to define the controller related to this URL
        $url = $this->adminUrlGenerator
            ->setController(ProductCrudController::class)
            ->setAction(Action::INDEX)
            ->generateUrl();

        // in applications containing more than one Dashboard, you must also
        // define the Dashboard associated to the URL
        $url = $this->adminUrlGenerator
            ->setDashboard(DashboardController::class)
            ->setController(ProductCrudController::class)
            ->setAction(Action::INDEX)
            ->generateUrl();

        // some actions may require to pass additional parameters
        $url = $this->adminUrlGenerator
            ->setController(ProductCrudController::class)
            ->setAction(Action::EDIT)
            ->setEntityId($product->getId())
            ->generateUrl();

        // ...
    }
}

这同样适用于在 Twig 模板中生成的 URL

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
{# if your application defines only one Dashboard #}
{% set url = ea_url()
    .setController('App\\Controller\\Admin\\ProductCrudController')
    .setAction('index') %}
{# if you prefer PHP constants, use this:
   .setAction(constant('EasyCorp\\Bundle\\EasyAdminBundle\\Config\\Action::INDEX')) #}

{# if your application defines multiple Dashboards #}
{% set url = ea_url()
    .setDashboard('App\\Controller\\Admin\\DashboardController')
    .setController('App\\Controller\\Admin\\ProductCrudController')
    .setAction('index') %}

{# some actions may require to pass additional parameters #}
{% set url = ea_url()
    .setController('App\\Controller\\Admin\\ProductCrudController')
    .setAction('edit')
    .setEntityId(product.id) %}
这项工作,包括代码示例,根据 Creative Commons BY-SA 3.0 许可证获得许可。
目录
    版本