Flag of Ukraine
SymfonyCasts stands united with the people of Ukraine

Override all the 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

Everything you see in EasyAdmin, from the layout of this table, to even how each individual field is rendered, is controlled by a template. EasyAdmin has a bunch of templates and we can override all of them!

We looked at these a bit earlier. Let's dive back into vendor/easycorp/easyadmin-bundle/src/Resources/views/. There's a lot of good stuff here, like layout.html.twig. This is the base layout file for every page. A few minutes ago, we also saw content.html.twig. This is a nice layout template that you can extend if you're creating a custom page inside of EasyAdmin.

In crud/, we see the templates for the individual pages and in field/, there's a template that controls how every field type is rendered.

In many of these templates, you'll see things like ea.templatePath('layout'). To understand that more deeply, hit "shift+shift" and open up a core class that we explored earlier called TemplateRegistry.

Internally, EasyAdmin maintains a map from a template "name" to an actual template path. So when you see something like ea.templatePath('layout'), that's going to use TemplateRegistry to figure out to load @EasyAdmin/layout, where @EasyAdmin is a Twig alias that points to this views/ directory.

Overriding a Core Template

With that in mind, here's our goal: I want to add a footer to the bottom of every page. Look again at layout.html.twig. Near the bottom, let's see... search for "footer". There we go! This has a block called content_footer. So if you define a content_footer, then it will get dumped right here. Let's override the layout.html.twig template and do that!

There are two ways to override a template. The first is to use Symfony's normal system for overriding templates that live inside of a bundle. You do that by creating a file in a very specific path. Inside of templates/, create a directory called bundles/... and inside of that, another directory with the name of the bundle: EasyAdminBundle. Now, match whatever path from the bundle that you want to override. Since we want to override layout.html.twig, create a new file called layout.html.twig.

But, hmm. I don't really want to override this entirely. I want to extend it. And, we can do that! Add {% extends %}, with @EasyAdmin/layout.html.twig. The only problem is that, by creating a templates/bundles/EasyAdminBundle/layout.html.twig file, when Symfony looks for the @EasyAdmin/layout.html.twig template, it will now use our file! In other words, we're extending ourselves!

To tell Symfony that we want to use the original template, add an exclamation point in front.

{% extends '@!EasyAdmin/layout.html.twig' %}
... lines 2 - 6

Perfect! Below, add {% block content_footer %} and {% endblock %}... with a nice little message inside.

... lines 1 - 2
{% block content_footer %}
Made with 🤍 by the guys and gals at SymfonyCasts
{% endblock %}

Let's try it! Refresh any page and... hello footer!

Overriding a Field Template

So that's the first way to override a template. The second is even more powerful because it allows us to control exactly when we want our overridden templates to be used. Like, you can override a template across your entire admin or just for one crud section. Let me close a few files.

So for our next trick, let's override the template that's used to render id fields. We're going to add a little key icon next to the ID.

Open up IdField.php. Ok, it sets its template name to crud/field/id. In the template registry... here it is... that corresponds to this template. So the template that renders IdField is Resources/views/crud/field/id.html.twig.

Instead of using the first method to override this - which would work - let's do something different.

Copy this template and create our override template... which could live anywhere. How about in templates/admin/field/... and call it id_with_icon.html.twig. Paste the contents... and I'll put the icon right before the id.

{# @var ea \EasyCorp\Bundle\EasyAdminBundle\Context\AdminContext #}
{# @var field \EasyCorp\Bundle\EasyAdminBundle\Dto\FieldDto #}
{# @var entity \EasyCorp\Bundle\EasyAdminBundle\Dto\EntityDto #}
{# this template is used to display Doctrine entity primary keys #}
<span class="fa fa-key"></span> {{ field.formattedValue }}

At this moment, this will not, yet be used. To activate it globally, go to DashboardController: you can configure template override paths down in configureCrud(). Check it out: ->overrideTemplate() where the first argument is the name of the template: that's the thing you see inside TemplateRegistry or IdField. So whenever EasyAdmin renders crud/field/id, we'll now have it point to admin/crud/field/id_with_icon.html.twig.

... lines 1 - 24
class DashboardController extends AbstractDashboardController
{
... lines 27 - 79
public function configureCrud(): Crud
{
return parent::configureCrud()
... lines 83 - 85
->overrideTemplate('crud/field/id', 'admin/field/id_with_icon.html.twig');
}
... lines 88 - 126
}

How cool is that? Let's try it! Refresh and... whoops... let me get rid of my extra crud/ path. Now let's try it! And... yes! Awesome! We see the key icon across the entire system.

Re-Using the Parent Template

But we can make this even better. The id template is super simple... since it just prints the formatted value. But sometimes the original template might do more complex stuff. Instead of repeating all of that in our template, we can include the original template. So quite literally include()... and I'll start typing id.html.twig... and let that autocomplete.

... lines 1 - 4
<span class="fa fa-key"></span>
{{ include('@EasyAdmin/crud/field/id.html.twig') }}

At the browser... we get the same result.

Next, let's talk about permissions: How we can deny access to entire CRUD controllers or specific actions based on the user's role.

Leave a comment!

13
Login or Register to join the conversation

Hello I hope you can help me,

I have two entities Category and CategoryTranslations

in CategoryTranslations set the description field yield TextEditorField::new('body')->setColumns(12);

in Category
yield CollectionField::new('translations')
->useEntryCrudForm()
->setColumns(12);

but when showing the Category form the text editor is not shown it is only shown when changing to the textarea type

A hug

1 Reply
akincer Avatar

If one wanted to use licensed Fontawesome icons in the admin section would this be how it could be done? Or does it require the free version?

Reply

Hey Aaron,

yes, you can use the paid version, you only need to specify the right CSS class and that should do the trick

Cheers!

Reply

There is a typo in the next challenge.

Option B says "but" instead of "both"

Cheers

Reply

Hey Julien!

Good catch! I just fixed that, and a few more minor typos in that challenge. Thank you for reporting it!

Cheers!

Reply
Tomáš S. Avatar
Tomáš S. Avatar Tomáš S. | posted 6 months ago

Hello,

I have created custom footer by overriding layout.html.twig.
When theme is switchet to dark, footer is displayed incorrectly (with white background).

What could be wrong?

Thank you

Reply

Hey Tomáš,

Most probably you're using custom css rules for your footer code, and so you need to write more extra code in css to cover dark mode colors for the css rules you're using there. It depends on how you implemented dark mode switching for your admin.

Cheers!

Reply
Tomáš S. Avatar

Hello,

I dont have custom css rules. I have only added src/bundles/EasyAdminBundle/layout.html.twig

with this code:

{% extends '@!EasyAdmin/layout.html.twig' %}

{% block content_footer %}
{{ '©' | raw }} {{ 'now' | date('Y') }} Created by Tomas
{% endblock %}

I was supprised that HTML tags there are not working. I expect they should work...

Regards Tomas

Reply

Hey Tomas,

This should work, at least I would expect this too. In this case there might be a style bug in EasyAdmin. The dark mode in that bundle is pretty fresh feature that was added recently. First of all, make sure you're using the latest version available. If you still have this issue - try to use dev-master to make sure that this bug wasn't fixed in master but still does not have a release. Only then try to report this as a bug.

I hope this help!

Cheers!

Reply
Tomáš S. Avatar

Hey Victor,

I have this version of EasyAdmin Bundle installed:
"name": "easycorp/easyadmin-bundle",
"version": "v4.0.9",

I think this one is the actualy latest.

I am sorry, but I am beginner in Symfony, Composer... Could you specify how can I switch to EasyAdminBundle to dev version and eventually back?

Thank you!

Cheers!

Reply

Hey Tomas,

You're right, it's the latest for now. Sure, let's figure out together. You can find the bundle on Packagist: https://packagist.org/packa... - as you can see in the right sidebar, there's the list of available releases, but on the first row you can see "4.x-dev" - that's the key. Specify that as the version:

$ symfony composer req easycorp/easyadmin-bundle:4.x-dev

It will install the bundle using the latest commit on that branch, for me it's:

> Downloading easycorp/easyadmin-bundle (4.x-dev 025bc2b)

Cheers!

Reply
Tomáš S. Avatar

Hello Victor,

I have reported a bug. Now, its fixed in dev version! Awesome!

Cheers

Reply

Hey Tomas,

Awesome! That was a good catch ;) Thank you for taking your time and reporting this upstream! And thanks for updating the thread on its progress!

Cheers!

Reply
Cat in space

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

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
    }
}