Configuring Fields

This Chapter isn't
quite ready...

Rest assured, the gnomes are hard at work
completing this video!

Browse Tutorials

Open up the "User" section. EasyAdmin has a concept of "fields". A field controls how a property is displayed on the index and detail pages, but also how it renders inside of a form. So the field completely defines the property inside the admin. By default, EasyAdmin just... guesses which fields to include. But usually you'll want to control this. How? Via the configureFields() method in the CRUD controller.

In this case, open UserCrudController.php... and you can see that it already has a commented-out configureFields() method. Go ahead and uncomment that.

Notice that you can either return an array or an iterable. I usually return an iterable by saying yield Field::new() and passing the property name, like id.

When I refresh... we have "ID" and nothing else.

Field Types

So EasyAdmin has many different types of fields, like text fields, boolean fields, and association fields... and it does its best to guess which type to use. In this case, you can't really see it, but when we said id, it guessed that this is an IdField. Instead of just saying Field::new() and letting it guess, I often prefer being explicit: IdField::new().

Watch: when we refresh... that makes absolutely no difference! It was already guessing that this was an IdField.

Cool! So how do we figure out what all of the field types are? Documentation is the most obvious way. If you look on the web debug toolbar, there's a little EasyAdmin icon. Click into that... to see some basic info about the page... with a handy link to the documentation. Open that up. It has a "Field Types" section down a ways. Yup, there's your big list of all the different field types inside of EasyAdmin.

Or, if you want to go rogue, you find this directly in the source code. Check out vendor/easycorp/easyadmin-bundle/src/Field. Here is the directory that holds all the different possible field types.

Back in our CRUD controller, let's add a few more fields.

If you look in the User entity, you can see $id, $email, $roles, $password, $enabled, $firstName, $lastName, $avatar... and then a couple of association fields. We won't need to manage all of these in the admin, but we will want most of them.

Add yield TextField::new('firstName')... repeat that for $lastName... and then for the $enabled field, let's yield BooleanField::new('enabled'). We also have a $createdAt field... so yield DateField::new('createdAt').

So I'm just listing the same properties that we see in the entity. Well, we don't see $createdAt... but that's only because it lives inside of the TimestampableEntity trait.

Anyways, with just this config, if we move over and refresh... beautiful! The text fields render normal text, the DateField knows how to print dates and the BooleanField gives us this nice little switch!

Using "Pseudo Properties"

As a challenge, instead of rendering "First Name" and "Last Name" columns, could we combine them into a single "Full Name" field? Let's try it!

I'll say yield TextField::new('fullName'). This is not a real property. If you open User, there is no $fullName property. But, I do have a getFullName() method. So the question is: is it smart enough - because the field is called fullName - to call the getFullName() method?

Let's find out. I bet you can guess the answer. Yup! That works!

Behind the scenes, EasyAdmin uses the PropertyAccessor component from Symfony. It's the same component that's used inside of the form system... and it's really good at reading properties by leveraging their getter method.

Field Options

Back in configureFields(), I forgot to add an "email" field. So, yield TextField::new('email'). And... no surprise, it renders correctly. But, this is a case where there's actually a more specific field for this: EmailField. The only difference is that it renders with a link to the email. And, when you look at the form, it will now be rendering as an <input type="email">.

The real power of fields is that each has a ton of options. Some field options are shared by all field types. For example, you can call ->addCssClass() on any field to add a CSS class to it. That's super handy. But other options are specific to the field type itself. For example, BooleanField has a ->renderAsSwitch() method... and we can pass this false. Now, instead of rendering this cute switch, it just says "YES". This... is probably a good idea anyways... because it was a bit too easy to accidentally disable a user before this.

So... this is great! We can control which fields are displayed and we know that there are methods we can call on each field object to configure its behavior. But remember, fields control both how things are rendered on the index and detail pages and how they're rendered on the form. Right now, if we go to the form... yup! That's what I expected: these are the five fields that we've configured.

It's not perfect, though. I do like having an "ID" column on my index page, but I do not like having an "ID" field in my form.

So next, let's learn how to only show certain fields on certain pages. We'll also learn a few more tricks for configuring them.

Leave a comment!

What PHP libraries does this tutorial use?

// composer.json
{
    "require": {
        "php": ">=8.0.2",
        "ext-ctype": "*",
        "ext-iconv": "*",
        "composer/package-versions-deprecated": "^1.11", // 1.11.99.4
        "doctrine/doctrine-bundle": "^2.1", // 2.5.5
        "doctrine/doctrine-migrations-bundle": "^3.0", // 3.2.1
        "doctrine/orm": "^2.7", // 2.10.4
        "easycorp/easyadmin-bundle": "^4.0", // v4.0.2
        "handcraftedinthealps/goodby-csv": "^1.4", // 1.4.0
        "knplabs/knp-markdown-bundle": "dev-symfony6", // dev-symfony6
        "knplabs/knp-time-bundle": "^1.11", // 1.17.0
        "sensio/framework-extra-bundle": "^6.0", // v6.2.5
        "stof/doctrine-extensions-bundle": "^1.4", // v1.7.0
        "symfony/asset": "6.0.*", // v6.0.1
        "symfony/console": "6.0.*", // v6.0.2
        "symfony/dotenv": "6.0.*", // v6.0.2
        "symfony/flex": "^2.0.0", // v2.0.1
        "symfony/framework-bundle": "6.0.*", // v6.0.2
        "symfony/mime": "6.0.*", // v6.0.2
        "symfony/monolog-bundle": "^3.0", // v3.7.1
        "symfony/runtime": "6.0.*", // v6.0.0
        "symfony/security-bundle": "6.0.*", // v6.0.2
        "symfony/stopwatch": "6.0.*", // v6.0.0
        "symfony/twig-bundle": "6.0.*", // v6.0.1
        "symfony/ux-chartjs": "^2.0", // v2.0.1
        "symfony/webpack-encore-bundle": "^1.7", // v1.13.2
        "symfony/yaml": "6.0.*", // v6.0.2
        "twig/extra-bundle": "^2.12|^3.0", // v3.3.7
        "twig/twig": "^2.12|^3.0" // v3.3.7
    },
    "require-dev": {
        "doctrine/doctrine-fixtures-bundle": "^3.3", // 3.4.1
        "symfony/debug-bundle": "6.0.*", // v6.0.2
        "symfony/maker-bundle": "^1.15", // v1.36.4
        "symfony/var-dumper": "6.0.*", // v6.0.2
        "symfony/web-profiler-bundle": "6.0.*", // v6.0.2
        "zenstruck/foundry": "^1.1" // v1.16.0
    }
}