跳到内容

字段

编辑此页

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

配置要显示的字段

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

在您的 CRUD 控制器中实现 configureFields() 方法以自定义要显示的字段列表

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
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 configureFields(string $pageName): iterable
    {
        // ...
    }

    // ...
}

有几种定义要显示的字段列表的方法。

选项 1. 返回带有要显示的属性名称的字符串。 EasyAdmin 会自动为它们创建字段并应用默认配置选项

1
2
3
4
5
6
7
8
9
10
public function configureFields(string $pageName): iterable
{
    return [
        'title',
        'description',
        'price',
        'stock',
        'publishedAt',
    ];
}

选项 2. 返回为 Doctrine 实体属性创建的 Field 对象。 EasyAdmin 将这些通用 Field 对象转换为用于显示每种属性类型的特定对象

1
2
3
4
5
6
7
8
9
10
11
12
use EasyCorp\Bundle\EasyAdminBundle\Field\Field;

public function configureFields(string $pageName): iterable
{
    return [
        Field::new('title'),
        Field::new('description'),
        Field::new('price'),
        Field::new('stock'),
        Field::new('publishedAt'),
    ];
}

选项 3. 返回适当的字段对象以显示每个属性

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
use EasyCorp\Bundle\EasyAdminBundle\Field\DateTimeField;
use EasyCorp\Bundle\EasyAdminBundle\Field\IntegerField;
use EasyCorp\Bundle\EasyAdminBundle\Field\MoneyField;
use EasyCorp\Bundle\EasyAdminBundle\Field\TextEditorField;
use EasyCorp\Bundle\EasyAdminBundle\Field\TextField;

public function configureFields(string $pageName): iterable
{
    return [
        TextField::new('title'),
        TextEditorField::new('description'),
        MoneyField::new('price')->setCurrency('EUR'),
        IntegerField::new('stock'),
        DateTimeField::new('publishedAt'),
    ];
}

字段构造函数的唯一强制参数是由此字段管理的 Doctrine 实体属性的名称。 EasyAdmin 使用 PropertyAccess 组件 来获取属性的值,因此实体可以将它们的访问定义为公共属性(例如 public $firstName)或公共方法(例如 public function getFirstName(), public function firstName())。

注意

EasyAdmin 使用 Symfony 表单来创建和编辑 Doctrine 实体。这就是为什么所有实体属性都必须可为空:它们的 setter 需要接受 null 值,并且它们的 getter 必须允许返回 null。在数据库中,关联的字段不必可为空。

未映射字段

字段通常引用相关 Doctrine 实体的属性。但是,它们也可以引用实体的方法,这些方法不与任何属性关联。例如,如果您的 Customer 实体定义了 firstNamelastName 属性,您可能想要显示一个合并了这两个值的“全名”字段。

为此,请将以下方法添加到实体

1
2
3
4
5
6
7
8
9
10
11
12
use Doctrine\ORM\Mapping as ORM;

#[ORM\Entity]
class Customer
{
    // ...

    public function getFullName()
    {
        return $this->getFirstName().' '.$this->getLastName();
    }
}

现在,添加一个引用此 getFullName() 方法的 fullName 字段。字段名称和方法之间的转换必须符合 PropertyAccess 组件 的规则(例如 foo_bar -> getFooBar()fooBar()

1
2
3
4
5
6
7
public function configureFields(string $pageName): iterable
{
    return [
        TextField::new('fullName'),
        // ...
    ];
}

请注意,未映射字段是不可排序的,因为它们不存在作为数据库表列,因此无法包含在 Doctrine 查询中。在某些情况下,您可以通过使用 SQL 计算未映射字段的内容来自行克服此限制。为此,请覆盖您的 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
namespace App\Controller\Admin;

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

class UserCrudController extends AbstractCrudController
{
    // ...

    public function configureFields(string $pageName): iterable
    {
        return [
            TextField::new('fullName'),
            // ...
        ];
    }

    public function createIndexQueryBuilder(SearchDto $searchDto, EntityDto $entityDto, FieldCollection $fields, FilterCollection $filters): QueryBuilder
    {
        $queryBuilder = parent::createIndexQueryBuilder($searchDto, $entityDto, $fields, $filters);

        // if user defined sort is not set
        if (0 === count($searchDto->getSort())) {
            $queryBuilder
                ->addSelect('CONCAT(entity.first_name, \' \', entity.last_name) AS HIDDEN full_name')
                ->addOrderBy('full_name', 'DESC');
        }

        return $queryBuilder;
    }
}

每页显示不同的字段

有几种方法可以根据当前页面有条件地显示字段

1
2
3
4
5
6
7
8
9
10
11
public function configureFields(string $pageName): iterable
{
    return [
        IdField::new('id')->hideOnForm(),
        TextField::new('firstName'),
        TextField::new('lastName'),
        TextField::new('phone'),
        EmailField::new('email')->hideOnIndex(),
        DateTimeField::new('createdAt')->onlyOnDetail(),
    ];
}

以下是所有可用的方法

  • hideOnIndex()
  • hideOnDetail()
  • hideOnForm()(在 editnew 页面中都隐藏字段)
  • hideWhenCreating()
  • hideWhenUpdating()
  • onlyOnIndex()
  • onlyOnDetail()
  • onlyOnForms()(在除 editnew 之外的所有页面中隐藏字段)
  • onlyWhenCreating()
  • onlyWhenUpdating()

如果每页上要显示的字段完全不同,请使用给定的 $pageName 参数来区分它们

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

public function configureFields(string $pageName): iterable
{
    $id = IdField::new('id');
    $firstName = TextField::new('firstName');
    $lastName = TextField::new('lastName');
    $phone = TextField::new('phone');
    $email = EmailField::new('email');
    $createdAt = DateTimeField::new('createdAt');

    if (Crud::PAGE_INDEX === $pageName) {
        return [$id, $firstName, $lastName, $phone];
    } elseif(Crud::PAGE_DETAIL === $pageName) {
        return ['...'];
    } else {
        return ['...'];
    }
}

如果您需要更大的控制,请考虑使用以下使用 PHP 生成器 定义字段的方式

1
2
3
4
5
6
7
8
9
10
11
12
13
public function configureFields(string $pageName): iterable
{
    yield IdField::new('id')->hideOnForm();

    if ('... some expression ...') {
        yield TextField::new('firstName');
        yield TextField::new('lastName');
    }

    yield TextField::new('phone');
    yield EmailField::new('email')->hideOnIndex();
    yield DateTimeField::new('createdAt')->onlyOnDetail();
}

字段布局

默认情况下,EasyAdmin 表单每行显示一个字段。在每一行内,字段根据其类型显示不同的宽度(例如,整数字段很窄,而代码编辑器字段非常宽)。

在本节中,您将学习如何自定义每个字段的宽度,以及借助标签页、列、字段集和行等元素自定义整个表单布局。

表单标签页

此元素旨在使非常长/复杂的表单更易于使用。它允许将字段分组到单独的标签页中,这些标签页一次可见一个。它看起来像这样

EasyAdmin form that uses tabs to group fields

使用特殊 FormField 对象的 addTab() 方法将标签页添加到您的表单

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
use EasyCorp\Bundle\EasyAdminBundle\Field\FormField;

public function configureFields(string $pageName): iterable
{
    return [
        // Creates a tab: all fields following it will belong to that tab
        // (until the end of the form or until you create another tab)
        FormField::addTab('First Tab'),
        TextField::new('firstName'),
        TextField::new('lastName'),

        // Creates a second tab and customizes some of its properties, such
        // as its icon, CSS class and help message
        FormField::addTab('Contact Information Tab')
            ->setIcon('phone')->addCssClass('optional')
            ->setHelp('Phone number is preferred'),

        TextField::new('phone'),
        // ...
    ];
}

addTab() 方法的参数是

  • $label:(类型:TranslatableInterface|string|false|null)此标签页在标签页的可点击列表中显示的文本;如果您将其设置为 falsenull 或空字符串,则不会显示任何文本(确保为标签页显示图标,否则用户将无法点击它);您还可以传递 stringTranslatableInterface 变量。在这两种情况下,如果它们包含 HTML 标签,它们将被渲染而不是转义;
  • $icon:(类型:?string)FontAwesome 图标的完整 CSS 类(例如 far fa-folder-open);如果您没有为标签页显示文本标签,请确保显示图标,否则用户将无法点击标签页。

注意

默认情况下,EasyAdmin 假定图标名称对应于 FontAwesome CSS 类。必要的 CSS 样式和 Web 字体也默认包含在内,因此您无需采取任何其他步骤即可使用 FontAwesome 图标。或者,您可以使用您自己的图标集而不是 FontAwesome。

在标签页内,您不仅可以包含表单字段,还可以包含以下部分中解释的所有其他表单布局字段:列、字段集和行。以下是使用所有这些元素的表单的外观

EasyAdmin form that uses tabs, columns, fieldsets and rows

默认情况下,标签页使用特殊的 Symfony 表单类型渲染。此类型的名称是 ea_form_tab + 随机 ULID 值。这使得无法使用表单主题覆盖其模板。要自定义它,请使用 addTab() 方法的 propertySuffix 可选参数

1
FormField::addTab('Contact Information Tab', propertySuffix: 'contact');

按照此示例,您可以定义以下块来覆盖此标签页的设计

{% endblock _MyEntity_ea_form_tab_contact_row %}

{% block _MyEntity_ea_form_tab_close_contact_row %}
{# ... #} {{ block('ea_form_tab_close_row') }} {# ... #}

{% endblock _MyEntity_ea_form_tab_close_contact_row %}

4.20

propertySuffix 参数在 EasyAdmin 4.20.0 中引入。

表单列

4.8.0

表单列在 EasyAdmin 4.8.0 中引入。

在使用此选项之前,您必须熟悉 Bootstrap 网格系统,它将每一行划分为 12 个等宽列,以及 Bootstrap 断点,它们是 xs(设备宽度 < 576px)、sm(>= 576px)、md(>= 768px)、lg(>= 992px)、xl(>= 1,200px)和 xxl(>= 1,400px)。

表单列允许将复杂的表单分解为两列或更多列字段。除了增加信息密度外,列还允许根据字段的功能更好地分隔字段。以下是三列表单的外观

EasyAdmin form that uses three columns to group fields

以下是一个简单的示例,它将表单分为两列(第一列跨越 12 个可用 Bootstrap 列中的 8 个,第二列跨越其他 4 个 Bootstrap 列)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
use EasyCorp\Bundle\EasyAdminBundle\Field\FormField;

public function configureFields(string $pageName): iterable
{
    return [
        FormField::addColumn(8),
        TextField::new('firstName'),
        TextField::new('lastName'),

        FormField::addColumn(4),
        TextField::new('phone'),
        TextField::new('email')->hideOnIndex(),
    ];
}

addColumn() 方法的参数是

  • $cols:(类型:int|string)列的宽度,定义为与 Bootstrap 网格系统 兼容的任何值(例如 'col-6''col-md-6 col-xl-4' 等)。整数值按如下方式转换:N -> 'col-N'(例如 8 转换为 col-8);
  • $label:(类型:TranslatableInterface|string|false|null)显示在列顶部的可选标题。如果您传递 falsenull 或空字符串,则不显示标题。您还可以传递 stringTranslatableInterface 变量。在这两种情况下,如果它们包含 HTML 标签,它们将被渲染而不是转义;
  • $icon:(类型:?string)FontAwesome 图标的完整 CSS 类(例如 far fa-folder-open),显示在列标签旁边;
  • $help:(类型:?string)显示在列标签下方的可选内容;它主要用于描述列内容或提供进一步的说明或帮助内容。您可以包含 HTML 标签,它们将被渲染而不是转义。

注意

默认情况下,EasyAdmin 假定图标名称对应于 FontAwesome CSS 类。必要的 CSS 样式和 Web 字体也默认包含在内,因此您无需采取任何其他步骤即可使用 FontAwesome 图标。或者,您可以使用您自己的图标集而不是 FontAwesome。

借助 Bootstrap 响应式类,您可以拥有不同大小的列,甚至完全没有列,具体取决于浏览器窗口大小。在以下示例中,lg 以下的断点不显示列。此外,两列的总和不合计 12;这允许创建比可用总空间短的列

1
2
3
4
5
6
7
8
9
10
11
12
13
14
use EasyCorp\Bundle\EasyAdminBundle\Field\FormField;

public function configureFields(string $pageName): iterable
{
    return [
        FormField::addColumn('col-lg-8 col-xl-6'),
        TextField::new('firstName'),
        TextField::new('lastName'),

        FormField::addColumn('col-lg-3 col-xl-2'),
        TextField::new('phone'),
        TextField::new('email')->hideOnIndex(),
    ];
}

您还可以在标签页内使用列,以进一步组织非常复杂的布局的内容

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
use EasyCorp\Bundle\EasyAdminBundle\Field\FormField;

public function configureFields(string $pageName): iterable
{
    return [
        FormField::addTab('User Data'),

        FormField::addColumn('col-lg-8 col-xl-6'),
        TextField::new('firstName'),
        TextField::new('lastName'),

        FormField::addColumn('col-lg-3 col-xl-2'),
        TextField::new('phone'),
        TextField::new('email')->hideOnIndex(),

        FormField::addTab('Financial Information'),

        // ...
    ];
}

注意

默认情况下,列内的所有字段都与其包含列一样宽。使用表单行(如下所述)自定义字段宽度和/或在同一行上显示多个字段。

默认情况下,列使用特殊的 Symfony 表单类型渲染。此类型的名称是 ea_form_column + 随机 ULID 值。这使得无法使用表单主题覆盖其模板。要自定义它,请使用 addColumn() 方法的 propertySuffix 可选参数

1
FormField::addColumn('col-lg-8 col-xl-6', propertySuffix: 'main');

按照此示例,您可以定义以下块来覆盖此列的设计

{% endblock _MyEntity_ea_form_column_main_row %}

{% block _MyEntity_ea_form_column_close_main_row %}
{# ... #} {{ block('ea_form_column_close_row') }} {# ... #}

{% endblock _MyEntity_ea_form_column_close_main_row %}

4.20

propertySuffix 参数在 EasyAdmin 4.20.0 中引入。

表单字段集

4.8.0

表单字段集在 EasyAdmin 4.8.0 中引入。在以前的版本中,此功能称为“表单面板”。

在显示大量字段的页面中,您可以使用字段集将它们分组。以下是它们的外观

EasyAdmin form that uses fieldsets to group fields into different sections

使用特殊 FormField 对象的 addFieldset() 方法创建字段集

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
use EasyCorp\Bundle\EasyAdminBundle\Field\FormField;

public function configureFields(string $pageName): iterable
{
    return [
        // fieldsets usually display only a title
        FormField::addFieldset('User Details'),
        TextField::new('firstName'),
        TextField::new('lastName'),

        // fieldsets without titles only display a separation between fields
        FormField::addFieldset(),
        DateTimeField::new('createdAt')->onlyOnDetail(),

        // fieldsets can also define their icon, CSS class and help message
        FormField::addFieldset('Contact information')
            ->setIcon('phone')->addCssClass('optional')
            ->setHelp('Phone number is preferred'),
        TextField::new('phone'),
        TextField::new('email')->hideOnIndex(),

        // fieldsets can be collapsible too (useful if your forms are long)
        // this makes the fieldset collapsible but renders it expanded by default
        FormField::addFieldset('Contact information')->collapsible(),
        // this makes the fieldset collapsible and renders it collapsed by default
        FormField::addFieldset('Contact information')->renderCollapsed(),
    ];
}

addFieldset() 方法的参数是

  • $label:(类型:TranslatableInterface|string|false|null)显示在字段集顶部的可选标题。如果您传递 falsenull 或空字符串,则不显示标题。您还可以传递 stringTranslatableInterface 变量。在这两种情况下,如果它们包含 HTML 标签,它们将被渲染而不是转义;
  • $icon:(类型:?string)FontAwesome 图标的完整 CSS 类(例如 far fa-folder-open),显示在字段集标签旁边。

注意

默认情况下,EasyAdmin 假定图标名称对应于 FontAwesome CSS 类。必要的 CSS 样式和 Web 字体也默认包含在内,因此您无需采取任何其他步骤即可使用 FontAwesome 图标。或者,您可以使用您自己的图标集而不是 FontAwesome。

当使用表单列时,其中的字段集显示略有不同的设计,以更好地对不同的字段进行分组。这就是为什么建议在您使用列时始终使用字段集。以下是它的外观

EasyAdmin form that uses three columns and several fieldsets to group fields

默认情况下,字段集使用特殊的 Symfony 表单类型渲染。此类型的名称是 ea_form_fieldset + 随机 ULID 值。这使得无法使用表单主题覆盖其模板。要自定义它,请使用 addFieldset() 方法的 propertySuffix 可选参数

1
FormField::addFieldset('Contact information', propertySuffix: 'contact');

按照此示例,您可以定义以下块来覆盖此字段集的设计

{% endblock _MyEntity_ea_form_fieldset_contact_row %}

{% block _MyEntity_ea_form_fieldset_close_contact_row %}
{# ... #} {{ block('ea_form_fieldset_close_row') }} {# ... #}

{% endblock _MyEntity_ea_form_fieldset_close_contact_row %}

4.20

propertySuffix 参数在 EasyAdmin 4.20.0 中引入。

表单行

在使用此选项之前,您必须熟悉 Bootstrap 网格系统,它将每一行划分为 12 个等宽列,以及 Bootstrap 断点,它们是 xs(设备宽度 < 576px)、sm(>= 576px)、md(>= 768px)、lg(>= 992px)、xl(>= 1,200px)和 xxl(>= 1,400px)。

表单行允许在同一行显示两个或多个字段。 它的外观如下所示

EasyAdmin form that uses rows to display several fields on the same row

假设您想在同一行显示两个名为 startsAtendsAt 的字段,每个字段跨越该行的 6 列。 这是您配置该布局的方式

1
2
3
4
5
6
7
8
9
10
11
use EasyCorp\Bundle\EasyAdminBundle\Field\DateTimeField;

public function configureFields(string $pageName): iterable
{
    return [
        // ...,

        DateTimeField::new('startsAt')->setColumns(6),
        DateTimeField::new('endsAt')->setColumns(6),
    ];
}

此示例在同一行上呈现这两个字段,但在 xssm 断点中除外,在这些断点中,每个字段都占据整行(因为设备宽度太小)。

如果您需要更好地控制取决于设备宽度的设计,则可以传递一个包含响应式 CSS 类的字符串,这些类定义了字段在不同断点处的宽度

1
2
3
4
5
6
7
8
9
10
11
use EasyCorp\Bundle\EasyAdminBundle\Field\DateTimeField;

public function configureFields(string $pageName): iterable
{
    return [
        // ...,

        DateTimeField::new('startsAt')->setColumns('col-sm-6 col-lg-5 col-xxl-3'),
        DateTimeField::new('endsAt')->setColumns('col-sm-6 col-lg-5 col-xxl-3'),
    ];
}

此示例添加了 col-sm-6 以覆盖默认的 EasyAdmin 行为,并在 sm 断点中也在同一行上显示这两个字段。 此外,它减少了较大断点(lgxxl)中的列数,以改善这些字段的呈现效果。

提示

您还可以使用与重新排序和偏移列相关的 CSS 类

1
yield DateTimeField::new('endsAt')->setColumns('col-sm-6 col-xxl-3 offset-lg-1 order-3');

由于 Bootstrap 网格的工作方式,当您手动配置字段列时,每行将包含尽可能多的字段。 如果一个字段占用 4 列,而下一个字段占用 3 列,则该行仍有 12 - 4 - 3 = 5 列来呈现其他字段。 如果下一个字段占用超过 5 列,它将在下一行呈现。

有时您需要更好地控制此自动布局。 例如,您可能希望在同一行上显示两个或多个字段,并确保该行上不显示其他字段,即使有足够的空间。 为此,请使用特殊 FormField 字段的 addRow() 方法来强制创建新行(下一个字段将强制在新行上呈现)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
use EasyCorp\Bundle\EasyAdminBundle\Field\BooleanField;
use EasyCorp\Bundle\EasyAdminBundle\Field\DateTimeField;
use EasyCorp\Bundle\EasyAdminBundle\Field\FormField;

public function configureFields(string $pageName): iterable
{
    return [
        // ...,

        DateTimeField::new('startsAt')->setColumns('col-sm-6 col-lg-5 col-xxl-3'),
        DateTimeField::new('endsAt')->setColumns('col-sm-6 col-lg-5 col-xxl-3'),
        FormField::addRow(),

        // you can pass the name of the breakpoint to add a row only on certain widths
        // FormField::addRow('xl'),

        // this field will always render on its own row, even if there's
        // enough space for it in the previous row in `lg`, `xl` and `xxl` breakpoints
        BooleanField::new('published')->setColumns(2),
    ];
}

默认情况下,行是使用特殊的 Symfony 表单类型呈现的。 此类型的名称是 ea_form_row + 随机 ULID 值。 这使得无法使用表单主题覆盖其模板。 要自定义它,请使用 addRow() 方法的 propertySuffix 可选参数

1
FormField::addRow('xl', propertySuffix: 'main');

按照此示例,您可以定义以下块来覆盖此行的设计

{% endblock _MyEntity_ea_form_row_main_row %}

{% block _MyEntity_ea_form_row_close_main_row %}
{# ... #} {{ block('ea_form_row_close_row') }} {# ... #}

{% endblock _MyEntity_ea_form_row_close_main_row %}

4.20

propertySuffix 参数在 EasyAdmin 4.20.0 中引入。

字段类型

这些是 EasyAdmin 提供的所有内置字段

Doctrine 类型和 EasyAdmin 字段之间的映射

下表显示了根据实体属性的 Doctrine DBAL 类型 建议使用的 EasyAdmin 字段

Doctrine 类型 推荐的 EasyAdmin 字段
array ArrayField
ascii_string TextField
bigint TextField
binary (不支持)
blob (不支持)
boolean BooleanField
date_immutable DateField
date DateField
datetime_immutable DateTimeField
datetime DateTimeField
datetimetz_immutable DateTimeField
datetimetz DateTimeField
datetinterval TextField
decimal NumberField
float NumberField
guid TextField
integer IntegerField
json_array ArrayField
json TextField, TextareaField, CodeEditorField
object TextField, TextareaField, CodeEditorField
simple_array ArrayField
smallint IntegerField
string TextField
text TextareaField, TextEditorField, CodeEditorField
time_immutable TimeField
time TimeField

除了这些之外,EasyAdmin 还包括用于特定值的其他字段类型

  • AvatarField, ColorField, CountryField, CurrencyField, EmailField, IdField, ImageField, LanguageField, LocaleField, SlugField, TelephoneField, TimezoneFieldUrlField 与 Doctrine 的 string 类型配合良好。
  • MoneyFieldPercentField 与 Doctrine 的 decimalfloatinteger 配合良好,具体取决于您存储数据的方式。
  • AssociationFieldCollectionFieldChoiceField 是特殊字段,分别对应于 Symfony 的 EntityTypeCollectionTypeChoiceType

提示

如果您想使用 Doctrine 的 自定义映射类型 之一,您应该创建一个 Symfony 的 自定义表单字段类型 和一个 EasyAdmin 的 自定义字段。 请注意,对于某些自定义映射类型,如果您需要它们,您还需要自定义 EasyAdmin 的搜索和过滤功能。

字段配置

本节显示了所有字段类型可用的配置选项。 此外,某些字段定义了其他配置选项,如 字段参考 中所示。

标签选项

字段构造函数的第二个可选参数是标签,它可以采用许多不同的值

  • 如果您未显式设置标签,EasyAdmin 会根据字段名称自动生成标签(例如“firstName”->“First Name”);
  • null:EasyAdmin 会根据字段名称自动生成标签(例如“firstName”->“First Name”);
  • 空字符串:该字段不显示任何标签,但会呈现一个空的 <label> 元素,以免弄乱表单布局;
  • false:该字段不显示任何标签,也不会呈现 <label> 元素。 这对于显示特殊的全宽字段(例如使用自定义字段模板创建的地图或宽表)非常有用;
  • 如果您显式设置标签,EasyAdmin 将使用该值; 内容可以包含 HTML 标签,它们将被呈现,而不是转义。 此外,您可以使用 Translatable 内容(例如 t('admin.form.labels.user')

以下是一些操作中的字段标签示例

// 未定义标签:自动生成标签(label = 'First Name') TextField::new('firstName'), // 标签为 null:自动生成标签(label = 'First Name') TextField::new('firstName', null),

// 标签为 false:不显示标签,也不呈现 <label> 元素 TextField::new('firstName', false),

// 显式设置标签:呈现其内容,包括 HTML 标签 TextField::new('firstName', 'Customer <b>Name</b>'),

设计选项

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
TextField::new('firstName', 'Name')
    // use this method if your field needs a specific form theme to render properly
    ->addFormTheme('@FOSCKEditor/Form/ckeditor_widget.html.twig')
    // you can add more than one form theme using the same method
    ->addFormTheme('theme1.html.twig', 'theme2.html.twig', 'theme3.html.twig')

    // CSS class/classes are applied to the field contents (in the 'index' page)
    // or to the row that wraps the contents (in the 'detail', 'edit' and 'new' pages)

    // use this method to add new classes to the ones applied by EasyAdmin
    ->addCssClass('text-large text-bold')
    // use this other method if you want to remove any CSS class added by EasyAdmin
    ->setCssClass('text-large text-bold')

    // this defines the Twig template used to render this field in 'index' and 'detail' pages
    // (this is not used in the 'edit'/'new' pages because they use Symfony Forms themes)
    ->setTemplatePath('admin/fields/my_template.html.twig')

    // useful for example to right-align numbers/money values (this setting is ignored in 'detail' page)
    ->setTextAlign('right')
;

CRUD 设计选项 类似,字段还可以加载 CSS 文件、Javascript 文件和 Webpack Encore 条目,并将 HTML 内容添加到后端页面的 <head> 和/或 <body> 元素中

1
2
3
4
5
6
7
TextField::new('firstName', 'Name')
    ->addCssFiles('bundle/some-bundle/foo.css', 'some-custom-styles.css')
    ->addJsFiles('admin/some-custom-code.js')
    ->addWebpackEncoreEntry('admin-maps')
    ->addHtmlContentToHead('<link rel="dns-prefetch" href="https://assets.example.com">')
    ->addHtmlContentToBody('<!-- generated at '.time().' -->')
;

默认情况下,这些 Web 资源在所有后端页面中加载。 如果您需要更精确的控制,请使用 Asset 类来定义资源

1
2
3
4
5
6
7
8
9
10
use EasyCorp\Bundle\EasyAdminBundle\Config\Asset;
// ...

TextField::new('firstName', 'Name')
    ->addCssFiles(Asset::new('bundle/some-bundle/foo.css')->ignoreOnForm()->htmlAttr('media', 'print'))
    ->addJsFiles(Asset::new('admin/some-custom-code.js')->onlyOnIndex()->defer())
    ->addWebpackEncoreEntries(Asset::new('admin-maps')->onlyWhenCreating()->preload())
    // you can even define the Symfony Asset package which the asset belongs to
    ->addCssFiles(Asset::new('some-path/bar.css')->package('legacy_assets'))
;

格式化选项

formatValue() 方法允许在 indexdetail 页面中呈现值之前将 PHP 可调用对象应用于该值

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
IntegerField::new('stock', 'Stock')
    // callbacks usually take only the current value as argument
    ->formatValue(function ($value) {
        return $value < 10 ? sprintf('%d **LOW STOCK**', $value) : $value;
    });

TextEditorField::new('description')
    // callables also receives the entire entity instance as the second argument
    ->formatValue(function ($value, $entity) {
        return $entity->isPublished() ? $value : 'Coming soon...';
    });

// in PHP 7.4 and newer you can use arrow functions
// ->formatValue(fn ($value) => $value < 10 ? sprintf('%d **LOW STOCK**', $value) : $value);
// ->formatValue(fn ($value, $entity) => $entity->isPublished() ? $value : 'Coming soon...');

其他选项

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
TextField::new('firstName', 'Name')
    // if TRUE, listing can be sorted by this field (default: TRUE)
    // unmapped fields cannot be sorted
    ->setSortable(false)

    // help message displayed for this field in the 'detail', 'edit' and 'new' pages
    ->setHelp('...')

    // sets the value of the `empty_data` option in the Symfony form
    // see https://symfony.ac.cn/doc/current/reference/forms/types/form.html#empty-data
    ->setEmptyData('Jane Doe')

    // the Symfony Form type used to render this field in 'edit'/'new' pages
    // (fields have good default values for this option, so you don't usually configure this)
    ->setFormType(TextType::class)

    // an array of parameters passed to the Symfony form type
    // (this only overrides the values of the passed form type options;
    // it leaves all the other existing type options unchanged)
    ->setFormTypeOptions(['option_name' => 'option_value'])

    // a custom HTML attribute added when rendering the field
    // e.g. setHtmlAttribute('data-foo', 'bar') renders a 'data-foo="bar"' attribute in HTML
    // On 'index' and 'detail' pages, the attribute is added to the field container:
    // <td> and div.field-group respectively
    // On 'new' and 'edit' pages, the attribute is added to the form field;
    // it's a shortcut for the equivalent setFormTypeOption('attr.data-foo', 'bar)
    ->setHtmlAttribute('attribute_name', 'attribute_value')

    // a key-value array of attributes to add to the HTML element
    ->setHtmlAttributes(['data-foo' => 'bar', 'autofocus' => 'autofocus'])

创建自定义字段

字段是一个实现 EasyCorp\Bundle\EasyAdminBundle\Contracts\Field\FieldInterface 的类。 尽管该接口仅要求实现一些方法,但您可能希望添加内置字段中提供的所有方法,以配置所有常见的字段选项。 您可以使用 EasyCorp\Bundle\EasyAdminBundle\Field\FieldTrait 来实现此目的。

假设您想创建一个自定义 MapField,它为给定的邮政地址呈现完整的地图。 这是您可以为该字段创建的类

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\Admin\Field;

use EasyCorp\Bundle\EasyAdminBundle\Contracts\Field\FieldInterface;
use EasyCorp\Bundle\EasyAdminBundle\Field\FieldTrait;
use Symfony\Component\Form\Extension\Core\Type\TextareaType;

final class MapField implements FieldInterface
{
    use FieldTrait;

    /**
     * @param TranslatableInterface|string|false|null $label
     */
    public static function new(string $propertyName, $label = null): self
    {
        return (new self())
            ->setProperty($propertyName)
            ->setLabel($label)

            // this template is used in 'index' and 'detail' pages
            ->setTemplatePath('admin/field/map.html.twig')

            // this is used in 'edit' and 'new' pages to edit the field contents
            // you can use your own form types too
            ->setFormType(TextareaType::class)
            ->addCssClass('field-map')

            // loads the CSS and JS assets associated to the given Webpack Encore entry
            // in any CRUD page (index/detail/edit/new). It's equivalent to calling
            // encore_entry_link_tags('...') and encore_entry_script_tags('...')
            ->addWebpackEncoreEntries('admin-field-map')

            // these methods allow to define the web assets loaded when the
            // field is displayed in any CRUD page (index/detail/edit/new)
            ->addCssFiles('js/admin/field-map.css')
            ->addJsFiles('js/admin/field-map.js')
        ;
    }
}

接下来,创建用于在 indexdetail CRUD 页面 中呈现字段的模板。 该模板可以使用任何 Twig 模板功能 和以下变量

  • ea,一个 EasyCorp\Bundle\EasyAdminBundle\Context\AdminContext 实例,它存储 admin 上下文,并且在所有后端模板中都可用;
  • field,一个 EasyCorp\Bundle\EasyAdminBundle\Dto\FieldDto 实例,它存储正在呈现的字段的配置和值;
  • entity,一个 EasyCorp\Bundle\EasyAdminBundle\Dto\EntityDto 实例,它存储字段所属实体的实例以及有关该 Doctrine 实体的其他有用数据。

注意

此模板不用于 editnew CRUD 页面,它们使用 Symfony 表单主题 来定义每个表单字段的显示方式。

就这样。 您现在可以在任何 CRUD 控制器中使用此字段

1
2
3
4
5
6
7
8
9
use App\Admin\MapField;

public function configureFields(string $pageName): iterable
{
    return [
        // ...
        MapField::new('shipAddress'),
    ];
}

自定义选项

如果您的字段在任何方面都是可配置的,您可以为其添加自定义选项。 添加选项的推荐方法是将它们的名称定义为字段对象中的公共常量,并使用 FieldTrait 中定义的 setCustomOption() 方法来设置它们的值。

假设上一节中定义的 MapField 允许使用 Google Maps 或 OpenStreetMap 来呈现地图。 您可以按如下方式添加该选项

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

use EasyCorp\Bundle\EasyAdminBundle\Contracts\Field\FieldInterface;
use Symfony\Component\Form\Extension\Core\Type\TextareaType;

final class MapField implements FieldInterface
{
    use FieldTrait;

    public const OPTION_MAP_PROVIDER = 'mapProvider';

    public static function new(string $propertyName, ?string $label = null): self
    {
        return (new self())
            // ...
            ->setCustomOption(self::OPTION_MAP_PROVIDER, 'openstreetmap')
        ;
    }

    public function useGoogleMaps(): self
    {
        $this->setCustomOption(self::OPTION_MAP_PROVIDER, 'google');

        return $this;
    }

    public function useOpenStreetMap(): self
    {
        $this->setCustomOption(self::OPTION_MAP_PROVIDER, 'openstreetmap');

        return $this;
    }
}

稍后,您可以通过字段 DTO 的 getCustomOptions() 方法访问这些选项。 例如,在 Twig 模板中

1
2
3
4
5
6
7
8
9
10
11
{# admin/field/map.html.twig #}
{% if 'google' === field.customOptions.get('mapProvider') %}
    {# ... #}
{% endif %}

{# if you defined the field options as public constants, you can access
   them in the template too (although resulting code is a bit verbose) #}
{% set map_provider_option = constant('App\\Admin\\MapField::OPTION_MAP_PROVIDER') %}
{% if 'google' === field.customOptions.get(map_provider_option) %}
    {# ... #}
{% endif %}

字段配置器

某些字段的默认选项取决于实体属性的值,该值仅在运行时可用。 这就是为什么您可以选择定义字段配置器的原因,该配置器是一个在呈现字段之前更新字段配置的类。

EasyAdmin 为其内置字段定义了许多配置器。 您也可以创建自己的配置器(用于配置您自己的字段和/或内置字段)。 字段配置器是实现 EasyCorp\Bundle\EasyAdminBundle\Contracts\Field\FieldConfiguratorInterface 的类。

实施后,为您的配置器定义一个 Symfony 服务,并使用 ea.field_configurator 标记对其进行标记。 可选地,您可以定义标记的 priority 属性,以便在内置配置器之前或之后运行您的配置器。

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