Flag of Ukraine
SymfonyCasts stands united with the people of Ukraine

Overriding Field Templates

Keep on Learning!

If you liked what you've learned so far, dive in!
Subscribe to get access to this tutorial plus
video, code and script downloads.

Start your All-Access Pass
Buy just this tutorial for $12.00

With a Subscription, click any sentence in the script to jump to that part of the video!

Login Subscribe

We know that a field describes both the form type that you see on the form and also how that field is rendered on the detail and index pages. We also know how easy it is to customize the form type. We can ->setFormType() to use a completely different type or ->setFormTypeOption() to configure that type.

We can also change a lot about how each renders on the detail and index pages. For example, let's play with this "Votes" field:

... lines 1 - 9
use EasyCorp\Bundle\EasyAdminBundle\Field\IntegerField;
class AnswerCrudController extends AbstractCrudController
{
... lines 14 - 18
public function configureFields(string $pageName): iterable
{
... lines 21 - 23
yield IntegerField::new('votes');
... lines 25 - 31
}
}

If I autocomplete the methods on this, we have options like ->setCssClass(), ->addWebpackEncoreEntries(), ->addHtmlContentsToBody(), and ->addHtmlContentsToHead(). You can even call ->setTemplatePath() to completely override how this field is rendered on the index and detail pages, which we'll do in a moment.

But also notice that there's ->setTemplatePath() and ->setTemplateName(). What's the difference?

Template "Names" and the Template Registry

To answer that question, I'm going to hit Shift + Shift and open up a core class from EasyAdmin called TemplateRegistry.php. If you don't see it, make sure to "Include non-project items".

Perfect! Internally, EasyAdmin has many templates and it maintains this "map" of template names to the template filename behind each. So when you call ->setTemplateName(), what you would pass is some other template name. For example, I could pass crud/field/money if I wanted to use that template instead of the normal one.

But you probably won't override the template name very often. Most of the time, if you want to completely control how a field is rendered, you'll call ->setTemplatePath().

Here's the plan: when "Votes" is rendered on the index and detail pages, I want to render a completely different template. Let's call it admin/field/votes.html.twig:

... lines 1 - 11
class AnswerCrudController extends AbstractCrudController
{
... lines 14 - 18
public function configureFields(string $pageName): iterable
{
... lines 21 - 23
yield IntegerField::new('votes')
->setTemplatePath('admin/field/votes.html.twig');
... lines 26 - 32
}
}

Ok! Time to create that. In templates/, add 2 new directories admin/field... and a new file: votes.html.twig. Inside, I don't really know what to put here yet, so I'll just put "💯 votes!"... and see what happens:

When we move over and refresh... there it is! We are now in complete control of the votes!

Digging into the Core Templates

But, if you're like me, you're probably wondering what we can do inside of here. What variables do we have access to? One important thing to realize (and you can see it here in TemplateRegistry.php) is that every single field has a corresponding template. If you need to extend or change how a field is rendered, looking into the core template is pretty handy.

For example, votes is an IntegerField. Whelp, there's a template called integer.html.twig. Close this template registry and... let's go find that! Open vendor/easycorp/easyadmin-bundle/src/... close up Field/ and instead open Resources/views/crud/field. Here is the list of all of the field templates in the system. You can also see other templates that are used to render other parts of EasyAdmin... and you can override these as well.

Open up integer.html.twig. Ok cool. Check out the collection of comments on top. I like this! It helps our editor (and us) to know which variables we have access to. Apparently, we have access to a field variable, which is that familiar FieldDto object we talked about earlier. All the integer template does is just... print field.formattedValue.

Customizing the Template

Copy these three lines and paste them into our votes.html.twig:

{# @var ea \EasyCorp\Bundle\EasyAdminBundle\Context\AdminContext #}
{# @var field \EasyCorp\Bundle\EasyAdminBundle\Dto\FieldDto #}
{# @var entity \EasyCorp\Bundle\EasyAdminBundle\Dto\EntityDto #}
... lines 4 - 5

Then instead of "💯 votes!", say field.formattedValue "votes":

{# @var ea \EasyCorp\Bundle\EasyAdminBundle\Context\AdminContext #}
{# @var field \EasyCorp\Bundle\EasyAdminBundle\Dto\FieldDto #}
{# @var entity \EasyCorp\Bundle\EasyAdminBundle\Dto\EntityDto #}
{{ field.formattedValue }} votes

And when we try this... beautiful! But I bet we can make this fancier! If the votes are negative, let's put a little thumbs down. And if positive, a thumbs up.

Take off the word "votes"... and add if field.. Hmm. What we want to get is the underlying value - the true integer, not necessarily the "formatted" value. We can get that by saying field.value.

So formattedValue is the string that would print on the page, while value is the actual underlying (in this case) integer. So if field.value >= 0, else, and endif:

... lines 1 - 3
{% if field.value >= 0 %}
... line 5
{% else %}
... line 7
{% endif %}
{{ field.formattedValue }}

If it is greater than zero, add an icon with fas fa-thumbs-up text-success. Copy that... and paste for our thumbs down with text-danger:

... lines 1 - 3
{% if field.value >= 0 %}
<i class="fas fa-thumbs-up text-success"></i>
{% else %}
<i class="fas fa-thumbs-down text-danger"></i>
{% endif %}
... lines 9 - 10

And... just like that, we're making this field render however we want. It doesn't change how it looks like inside of the form (that's entirely handled by the form type), but it does control how it's rendered on the index page, and the detail page.

But, hmm. We also have a "votes" field inside of the Questions section. While it would be pretty easy to also point that votes field to the same new template, instead, I want to create a brand new custom field in EasyAdmin. That's next.

Leave a comment!

26
Login or Register to join the conversation

Whoops, we're sorry about that! It should be good now, I re-upload the video. Let us know if you still have some troubles with playing it

Cheers!

Reply

The error still here...

Please check my screen: https://ibb.co/fFPLk8S

Reply

Hey Dzianisr,

We're sorry about that! Looks like uploading was incomplete, I just re-uploaded this video again, now it should work!

Cheers!

Reply

Thanks for confirming, and sorry for the inconvenience!

Cheers!

1 Reply
seb-jean Avatar
seb-jean Avatar seb-jean | posted 1 year ago

Hi, video not works : "The media could not be loaded, either because the server or network failed or because the format is not supported."

1 Reply

Hey Sébastien,

Thank you for reporting this! The video was re-uploaded, it should be fixed now. We're sorry for the inconveniences! Let us know if you still have any troubles with playing it.

Cheers!

Reply
seb-jean Avatar
seb-jean Avatar seb-jean | Victor | posted 1 year ago | edited

It's good. Thanks victor

1 Reply

Thank you for confirming it works for you now!

Cheers!

Reply
seb-jean Avatar

The error came back :(

1 Reply

Hey Sébastien,

I'm sorry for that again! That's weird, but I just re-uploaded the video again, it should work now... at least it works for me.

Cheers!

Reply
seb-jean Avatar

Victor,
It works again!
Thanks!

Reply

Awesome!

Cheers!

Reply
EinzigTech Avatar
EinzigTech Avatar EinzigTech | Victor | posted 1 year ago

I still have the issue.

Reply

Hey Stiff,

I fixed it again! We're sorry about that, now it should work.

Cheers!

1 Reply
EinzigTech Avatar
EinzigTech Avatar EinzigTech | Victor | posted 1 year ago

Hi Victor,

Just to confirm, it is working now.

Best,
Stiff

Reply

Thanks for confirming!

Cheers!

1 Reply
Omar-Drb Avatar
Omar-Drb Avatar Omar-Drb | posted 8 months ago

Hi Ryan,
thanks for the awesome content as always!

We saw that we can use setTemplatePath() to set our own template for a field in the ['index', 'detail'] pages.
But is there a way to set a custom template for a field in the post pages? I tried to dig a little bit in the library but I got a bit lost.

If the answer is related to setFormType() then how can we create our own TypeClass with its custom template?

I want to convert my user roles json field to something like this: https://i.stack.imgur.com/26TlM.png

Thanks a lot in advanced :)

Reply

Hey Omar!

Excellent question! You're 100% right that setTemplatePath() is NOT the way - that's only for "rendering"/displaying the field - it doesn't affect how the form renders at all.

The form is rendered entirely via the form system. In fact, here is the template that, for example, renders the "edit" form: https://github.com/EasyCorp/EasyAdminBundle/blob/15c4125818b738785920c87029048ebf31e3b45c/src/Resources/views/crud/edit.html.twig#L61

And so, if you want to change how a field renders, you should create a custom form theme for this. I actually answered a similar question recently (honestly, I should have covered this in the tutorial) https://symfonycasts.com/screencast/easyadminbundle/association-many#comment-28419

The process is basically:

A) Use the web debug toolbar to find the name of the block(s) you can use to override how this one field renders
B) Create a template and render the field (and blocks) inside. This is probably the hardest part and has nothing to do with EasyAdmin - it is 100% about learning how to render a field in a custom way via a form theme https://symfony.com/doc/current/form/form_themes.html and https://symfonycasts.com/screencast/symfony-forms/form-theme-create
C) Call ->setFormTheme() on your field as an easy way to make sure your new template is included when the field is rendered.

Let me know if this helps!

Cheers!

1 Reply
Omar-Drb Avatar

Got it!!!
Thanks a lot this amazing content, honestly I'v never enjoyed any course as much as I do in SymfonyCasts, It's super useful, full of actually important tips and simply fun! I really appreciate the content thx 🤩

1 Reply

Yay! So happy to hear - thanks for letting me know. And happy new year!

1 Reply
Cat in space

"Houston: no signs of life"
Start the conversation!

What PHP libraries does this tutorial use?

// composer.json
{
    "require": {
        "php": ">=8.1.0",
        "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
    }
}
userVoice