Fields
Fields allow to display the contents of your Doctrine entities on each CRUD page. EasyAdmin provides built-in fields to display all the common data types, but you can also create your own fields.
Configuring the Fields to Display
If your CRUD controller extends from the AbstractCrudController
provided by EasyAdmin, the fields are configured automatically. In the index
page you'll see a few fields and in the rest of pages you'll see as many fields
as needed to display all the properties of your Doctrine entity.
Implement the configureFields()
method in your CRUD controller to customize
the list of fields to display:
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
{
// ...
}
// ...
}
There are several ways of defining the list of fields to display.
Option 1. Return strings with the name of the properties to display. EasyAdmin creates fields automatically for them and applies the default config options:
1 2 3 4 5 6 7 8 9 10
public function configureFields(string $pageName): iterable
{
return [
'title',
'description',
'price',
'stock',
'publishedAt',
];
}
Option 2. Return Field
objects created for the Doctrine entity properties.
EasyAdmin transforms these generic Field
objects into the specific objects
used to display each type of property:
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'),
];
}
Option 3. Return the appropriate field objects to display each property:
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'),
];
}
The only mandatory argument of the field constructors is the name of the
Doctrine entity property managed by this field. EasyAdmin uses the
PropertyAccess component to get the value of the properties, so the entity
can define their access as public properties (e.g. public $firstName
) or
public methods (e.g. public function getFirstName()
, public function
firstName()
).
Note
EasyAdmin uses Symfony Forms to create and edit Doctrine entities. That's
why all entity properties must be nullable: their setters need to accept
null
values and their getters must be allowed to return null
. In the
database, the associated fields don't have to be nullable.
Unmapped Fields
Fields usually reference to properties of the related Doctrine entity. However,
they can also refer to methods of the entity which are not associated to any
properties. For example, if your Customer
entity defines the firstName
and lastName
properties, you may want to display a "Full Name" field with
both values merged.
To do so, add the following method to the entity:
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();
}
}
Now, add a fullName
field that refers to this getFullName()
method. The
conversion between field names and methods must comply with the rules of the
PropertyAccess component (e.g. foo_bar
-> getFooBar()
or fooBar()
):
1 2 3 4 5 6 7
public function configureFields(string $pageName): iterable
{
return [
TextField::new('fullName'),
// ...
];
}
Beware that unmapped fields are not sortable because they don't exist as a
database table column, so they cannot be included in the Doctrine query. In some
cases, you can overcome this limitation yourself by computing the unmapped field
contents using SQL. To do so, override the createIndexQueryBuilder()
method
used in your CRUD controller:
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;
}
}
Displaying Different Fields per Page
There are several methods to display fields conditionally depending on the current page:
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(),
];
}
These are all the available methods:
hideOnIndex()
hideOnDetail()
hideOnForm()
(hides the field both inedit
andnew
pages)hideWhenCreating()
hideWhenUpdating()
onlyOnIndex()
onlyOnDetail()
onlyOnForms()
(hides the field in all pages exceptedit
andnew
)onlyWhenCreating()
onlyWhenUpdating()
If the fields to display are completely different on each page, use the given
$pageName
argument to differentiate them:
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 ['...'];
}
}
If you need even greater control, consider using the following way of defining the fields using PHP generators:
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();
}
Field Layout
By default, EasyAdmin forms displays one field per row. Inside each row, fields show a different width depending on their type (e.g. integer fields are narrow and code editor fields are very wide).
In this section, you'll learn how to customize the width of each field and also the whole form layout thanks to elements such as tabs, columns, fieldsets and rows.
Form Tabs
This element is intended to make very long/complex form more usable. It allows to group fields into separate tabs that are visible one at a time. It looks like this:
Add tabs to your forms with the addTab()
method of the special FormField
object:
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'),
// ...
];
}
The arguments of the addTab()
method are:
$label
: (type:TranslatableInterface|string|false|null
) the text that this tab displays in the clickable list of tabs; if you set it tofalse
,null
or an empty string, no text will be displayed (make sure to show an icon for the tab or users won't be able to click on it); You can also passstring
andTranslatableInterface
variables. In both cases, if they contain HTML tags they will be rendered in stead of escaped;$icon
: (type:?string
) the full CSS class of a FontAwesome icon (e.g.far fa-folder-open
); if you don't display a text label for the tab, make sure to display an icon or users won't be able to click on the tab.
Inside tabs you can include not only form fields but all the other form layout fields explained in the following sections: columns, fieldsets and rows. This is how a form using all those elements looks like:
Form Columns
4.8.0
Form columns were introduced in EasyAdmin 4.8.0.
Before using this option, you must be familiar with the Bootstrap grid system,
which divides each row into 12 same-width columns, and the Bootstrap breakpoints,
which are xs
(device width < 576px), sm
(>= 576px), md
(>= 768px),
lg
(>= 992px), xl
(>= 1,200px) and xxl
(>= 1,400px).
Form columns allows to break down a complex form into two or more columns of fields. In addition to increasing the density of information, columns allow to better separate fields according to their function. This is how a three column form looks like:
The following is a simple example that divides a form in two columns (the first one spanning 8 of the available 12 Bootstrap columns and the second column spanning the other 4 Bootstrap columns):
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(),
];
}
The arguments of the addColumn()
method are:
$cols
: (type:int|string
) the width of the column defined as any value compatible with the Bootstrap grid system (e.g.'col-6'
,'col-md-6 col-xl-4'
, etc.). Integer values are transformed like this: N -> 'col-N' (e.g.8
is transformed tocol-8
);$label
: (type:TranslatableInterface|string|false|null
) an optional title that is displayed at the top of the column. If you passfalse
,null
or an empy string, no title is displayed. You can also passstring
andTranslatableInterface
variables. In both cases, if they contain HTML tags they will be rendered in stead of escaped;$icon
: (type:?string
) the full CSS class of a FontAwesome icon (e.g.far fa-folder-open
) that is displayed next to the column label;$help
: (type:?string
) an optional content that is displayed below the column label; it's mostly used to describe the column contents or provide further instructions or help contents. You can include HTML tags and they will be rendered instead of escaped.
Thanks to Bootstrap responsive classes, you can have columns of different sizes,
or even no columns at all, depending on the browser window size. In the following
example, breakpoints below lg
doesn't display columns. Also, the sum of the
two columns doesn't total 12
; this is allowed to create columns shorter than
the total space available:
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(),
];
}
You can also use columns inside tabs to further organize the contents of very complex layouts:
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'),
// ...
];
}
Note
By default, all fields inside columns are as wide as their containing column. Use form rows, as explained below, to customize the field width and/or to display more than one field on the same row.
Form Fieldsets
4.8.0
Form fieldsets were introduced in EasyAdmin 4.8.0. In previous versions, this feature was called "Form Panels".
In pages where you display lots of fields, you can divide them in groups using fieldsets. This is how they look like:
Add fieldsets with the created with the addFieldset()
method of the special
FormField
object:
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(),
];
}
The arguments of the addFieldset()
method are:
$label
: (type:TranslatableInterface|string|false|null
) an optional title that is displayed at the top of the fieldset. If you passfalse
,null
or an empy string, no title is displayed. You can also passstring
andTranslatableInterface
variables. In both cases, if they contain HTML tags they will be rendered in stead of escaped;$icon
: (type:?string
) the full CSS class of a FontAwesome icon (e.g.far fa-folder-open
) that is displayed next to the fieldset label.
When using form columns, fieldsets inside them display a slightly different design to better group the different fields. That's why it's recommended to use fieldsets whenever you use columns. This is how it looks like:
Form Rows
Before using this option, you must be familiar with the Bootstrap grid system,
which divides each row into 12 same-width columns, and the Bootstrap breakpoints,
which are xs
(device width < 576px), sm
(>= 576px), md
(>= 768px),
lg
(>= 992px), xl
(>= 1,200px) and xxl
(>= 1,400px).
Form rows allow to display two or more fields on the same row. This is how it looks like:
Imagine that you want to display two fields called startsAt
and endsAt
on the same row, each of them spanning 6 columns of the row. This is how you
configure that layout:
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),
];
}
This example renders both fields on the same row, except in xs
and sm
breakpoints, where each field takes the entire row (because the device width is
too small).
If you need a better control of the design depending on the device width, you can pass a string with the responsive CSS classes that define the width of the field on different breakpoints:
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'),
];
}
This example adds col-sm-6
to override the default EasyAdmin behavior and
display the two fields on the same row also in the sm
breakpoint. Besides,
it reduces the number of columns in larger breakpoints (lg
and xxl
) to
improve the rendering of those fields.
Tip
You can also use the CSS classes related to reordering and offseting columns:
1
yield DateTimeField::new('endsAt')->setColumns('col-sm-6 col-xxl-3 offset-lg-1 order-3');
Because of how Bootstrap grid works, when you configure field columns manually,
each row will contain as many fields as possible. If one field takes 4 columns
and the next one takes 3 columns, the row still has 12 - 4 - 3 = 5
columns
to render other fields. If the next field takes more than 5 columns, it renders
on the next row.
Sometimes you need a better control of this automatic layout. For example, you
might want to display two or more fields on the same row, and ensure that no
other field is displayed on that row, even if there's enough space for it.
To do so, use the addRow()
method of the special FormField
field to
force the creation of a new line (the next field will forcibly render on a new row):
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),
];
}
Field Types
These are all the built-in fields provided by EasyAdmin:
- ArrayField
- AssociationField
- AvatarField
- BooleanField
- ChoiceField
- CodeEditorField
- CollectionField
- ColorField
- CountryField
- CurrencyField
- DateField
- DateTimeField
- EmailField
- HiddenField
- IdField
- ImageField
- IntegerField
- LanguageField
- LocaleField
- MoneyField
- NumberField
- PercentField
- SlugField
- TelephoneField
- TextareaField
- TextEditorField
- TextField
- TimeField
- TimezoneField
- UrlField
Mapping Between Doctrine Types and EasyAdmin Fields
The following table shows the recommended EasyAdmin field(s) to use depending on the Doctrine DBAL Type of your entity properties:
Doctrine Type | Recommended EasyAdmin Fields |
---|---|
array |
ArrayField |
ascii_string |
TextField |
bigint |
TextField |
binary |
(not supported) |
blob |
(not supported) |
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 |
In addition to these, EasyAdmin includes other field types for specific values:
AvatarField
,ColorField
,CountryField
,CurrencyField
,EmailField
,IdField
,ImageField
,LanguageField
,LocaleField
,SlugField
,TelephoneField
,TimezoneField
andUrlField
work well with Doctrine'sstring
type.MoneyField
andPercentField
work well with Doctrine'sdecimal
,float
andinteger
, depending on how do you store the data.AssociationField
,CollectionField
andChoiceField
are special fields that correspond to Symfony'sEntityType
,CollectionType
andChoiceType
respectively.
Tip
If you want to use one of Doctrine's Custom Mapping Types you should create one of Symfony's Custom Form Field Types and one of EasyAdmin's Custom Fields. Note that for some custom mapping types you will also need to customize EasyAdmin's search and filter functionality if you need them.
Field Configuration
This section shows the config options available for all field types. In addition, some fields define additional config options, as shown in the fields reference.
Label Options
The second optional argument of the field constructors is the label, which can take many different values:
- If you don't set the label explicitly, EasyAdmin generates the label automatically based on the field name (e.g. 'firstName' -> 'First Name');
- null: EasyAdmin generates the label automatically based on the field name (e.g. 'firstName' -> 'First Name');
- An empty string: the field doesn't display any label, but and empty
<label>
element is rendered to not mess with the form layout; - false: the field doesn't display any label and no
<label>
element is rendered either. This is useful to display special full-width fields such as maps or wide tables created with custom field templates; - If you set the label explicitly, EasyAdmin will use that value; the contents
can include HTML tags and they will be rendered, not escaped. Also, you can use
Translatable
contents (e.g.t('admin.form.labels.user')
)
Here are some examples of field labels in action:
// label not defined: generate it automatically (label = 'First Name') TextField::new('firstName'), // label is null: generate it automatically (label = 'First Name') TextField::new('firstName', null),
// label is false: no label is displayed and no <label> element is rendered TextField::new('firstName', false),
// label is set explicitly: render its contents, including HTML tags TextField::new('firstName', 'Customer <b>Name</b>'),
Design Options
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')
;
Similar to the CRUD design options, fields
can also load CSS files, Javascript files and Webpack Encore entries and add HTML
contents to the <head>
and/or <body>
elements of the backend pages:
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().' -->')
;
By default, these web assets are loaded in all backend pages. If you need a more
precise control, use the Asset
class to define the assets:
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'))
;
Formatting Options
The formatValue()
method allows to apply a PHP callable to the value before
rendering it in the index
and detail
pages:
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...');
Misc. Options
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.com/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'])
Creating Custom Fields
A field is a class that implements
EasyCorp
. Although the
interface only requires to implement a few methods, you may want to add all the
methods available in built-in fields to configure all the common field options.
You can use the EasyCorp
for that.
Imagine that you want to create a custom MapField
that renders a full map
for a given postal address. This is the class you could create for the field:
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')
;
}
}
Next, create the template used to render the field in the index
and detail
CRUD pages. The template can use any Twig templating features
and the following variables:
ea
, aEasyCorp
instance which stores the admin context and it's available in all backend templates;\Bundle \EasyAdminBundle \Context \AdminContext field
, aEasyCorp
instance which stores the config and value of the field being rendered;\Bundle \EasyAdminBundle \Dto \FieldDto entity
, aEasyCorp
instance which stores the instance of the entity which the field belongs to and other useful data about that Doctrine entity.\Bundle \EasyAdminBundle \Dto \EntityDto
Note
This template is not used in the edit
and new
CRUD pages,
which use Symfony Form themes to define how each form field is displayed.
That's all. You can now use this field in any of your CRUD controllers:
1 2 3 4 5 6 7 8 9
use App\Admin\MapField;
public function configureFields(string $pageName): iterable
{
return [
// ...
MapField::new('shipAddress'),
];
}
Custom Options
If your field is configurable in any way, you can add custom options for it.
The recommended way of adding options is defining their names as public constants
in the field object and use the setCustomOption()
method defined in the
FieldTrait
to set their values.
Imagine that the MapField
defined in the previous section allows to use
either Google Maps or OpenStreetMap to render the maps. You can add that
option as follows:
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;
}
}
Later you can access these options via the getCustomOptions()
method of the
field DTO. For example, in a Twig template:
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 %}
Field Configurators
Some default options of some fields depend on the value of the entity property, which is only available during runtime. That's why you can optionally define a field configurator, which is a class that updates the config of the field before rendering them.
EasyAdmin defines lots of configurators for its built-in fields. You can create
your own configurators too (either to configure your own fields and/or the
built-in fields). Field configurators are classes that implement
EasyCorp
.
Once implemented, define a Symfony service for your configurator and tag it with
the ea.field_configurator
tag. Optionally you can define the priority
attribute of the tag to run your configurator before or after the built-in ones.