Buy Access to Course
23.

Customizing the Menu

Share this awesome video!

|

Keep on Learning!

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

Login Subscribe

What else can we do with the menu? Well, of course, we can expand the simple entity items to get more control. To expand it, add entity: User. Now we can go crazy with label: Users:

177 lines | app/config/config.yml
// ... lines 1 - 80
easy_admin:
// ... line 82
design:
// ... lines 84 - 94
menu:
// ... line 96
- { entity: 'User', label: 'Users' }
// ... lines 98 - 177

Remember, you can also specify the label under the User entity itself. If we added a label key here, it would apply to the menu, the header to that section and a few other places. But under menu, it just changes the menu text.

Do the same thing for GenusNote, with label: Notes, and also for sub families: entity: SubFamily, label: Sub-Families:

177 lines | app/config/config.yml
// ... lines 1 - 80
easy_admin:
// ... line 82
design:
// ... lines 84 - 94
menu:
// ... lines 96 - 99
- { entity: 'GenusNote', label: 'Notes' }
- { entity: 'SubFamily', label: 'Sub-Families' }
// ... lines 102 - 177

At this point, it should be no surprise that we can control the icon for each menu. Like, icon: user, icon: sticky-note and icon: '':

177 lines | app/config/config.yml
// ... lines 1 - 80
easy_admin:
// ... line 82
design:
// ... lines 84 - 94
menu:
// ... line 96
- { entity: 'User', label: 'Users', icon: 'user' }
// ... lines 98 - 99
- { entity: 'GenusNote', label: 'Notes', icon: 'sticky-note' }
- { entity: 'SubFamily', label: 'Sub-Families', icon: '' }
// ... lines 102 - 177

Before configuring anything, each item has a little arrow icon. With empty quotes, even that icon is gone.

Adding Menu Separators & Sub-Menus

Oh, but the fanciness does, not, stop! The menu does a lot more than just simple links: it has separators, groups and sub-links. Above Genus, create a new item that only has a label:

178 lines | app/config/config.yml
// ... lines 1 - 80
easy_admin:
// ... line 82
design:
// ... lines 84 - 94
menu:
// ... line 96
- { entity: 'User', label: 'Users', icon: 'user' }
- { label: 'Genus' }
// ... lines 99 - 178

Yep, this has no route key and no entity key. We're not linking to anything.

Instead, this just adds a nice separator. Or, you can go a step further and create a sub-menu. Change this new menu item to use the expanded format. Then, add a children key. Indent all the other links so that they live under this:

181 lines | app/config/config.yml
// ... lines 1 - 80
easy_admin:
// ... line 82
design:
// ... lines 84 - 94
menu:
// ... line 96
- { entity: 'User', label: 'Users', icon: 'user' }
-
label: 'Genus'
children:
- Genus
- GenusHorde
// ... line 103
- { entity: 'GenusNote', label: 'Notes', icon: 'sticky-note' }
- { entity: 'SubFamily', label: 'Sub-Families', icon: '' }
// ... lines 106 - 181

And just to make it even nicer, add a separator called Related:

181 lines | app/config/config.yml
// ... lines 1 - 80
easy_admin:
// ... line 82
design:
// ... lines 84 - 94
menu:
// ... line 96
- { entity: 'User', label: 'Users', icon: 'user' }
-
label: 'Genus'
children:
- Genus
- GenusHorde
- { label: 'Related' }
- { entity: 'GenusNote', label: 'Notes', icon: 'sticky-note' }
- { entity: 'SubFamily', label: 'Sub-Families', icon: '' }
// ... lines 106 - 181

Try it! Try it! Nice! The Genus menu expands to show the sub-items and the Related separator.

We can also link to the different entity sections, but with different sort options. We already have a Genus link that will take us to that page with the normal sort. But let's not limit ourselves! We could also add another link to that same section, with a different label: Genuses (sorted by ID) and a params key:

188 lines | app/config/config.yml
// ... lines 1 - 80
easy_admin:
// ... line 82
design:
// ... lines 84 - 94
menu:
// ... lines 96 - 97
-
label: 'Genus'
children:
- Genus
-
entity: 'Genus'
label: 'Genuses (sorted by ID)'
params:
// ... lines 106 - 188

Here, we can control whatever query parameters we want, like sortField: id, sortDirection: ASC and... heck pizza: delicious:

188 lines | app/config/config.yml
// ... lines 1 - 80
easy_admin:
// ... line 82
design:
// ... lines 84 - 94
menu:
// ... lines 96 - 97
-
label: 'Genus'
children:
- Genus
-
entity: 'Genus'
label: 'Genuses (sorted by ID)'
params:
sortField: 'id'
sortDirection: 'ASC'
pizza: 'delicious'
// ... lines 109 - 188

That last query parameter won't do anything... but it doesn't make it any less true!

Ok, refresh! Then try out that new link. Yea! We're sorting by id and you might also notice in the address bar that pizza=delicious.

On that note, one of the other query parameters is action, which we can also set to anything. Copy this entire new menu link and - at the top of children - paste it. This time, let's link to the show page of 1 specific genus... our favorite "Pet genus". To do that, set action to show and id to some id in the database, like 2:

195 lines | app/config/config.yml
// ... lines 1 - 80
easy_admin:
// ... line 82
design:
// ... lines 84 - 94
menu:
// ... lines 96 - 97
-
label: 'Genus'
children:
-
entity: 'Genus'
label: 'Pet genus'
icon: 'paw'
params:
action: 'show'
id: 2
- Genus
// ... lines 109 - 195

This isn't anything special, we're just taking advantage of how the query parameters work in EasyAdminBundle.

And while we're here, it might also be nice to add a link to the front-end of our app. This is also nothing special: add a new link that points to the app_genus_list route called "Open front-end":

196 lines | app/config/config.yml
// ... lines 1 - 80
easy_admin:
// ... line 82
design:
// ... lines 84 - 94
menu:
// ... line 96
- { label: 'Open front-end', route: 'app_genus_list' }
// ... lines 98 - 196

Refresh! And try that link. Nice!

In addition to routes, if you want, you can just link to external URLs. Go to the bottom of the list... and make sure we're at the root level. Add a new section called "Important stuff" with icon: explanation and a children key:

208 lines | app/config/config.yml
// ... lines 1 - 80
easy_admin:
// ... line 82
design:
// ... lines 84 - 94
menu:
// ... lines 96 - 120
-
label: 'Important stuff'
icon: 'exclamation'
children:
// ... lines 125 - 208

I'll paste a couple of very important external links for silly kittens and wet cats:

208 lines | app/config/config.yml
// ... lines 1 - 80
easy_admin:
// ... line 82
design:
// ... lines 84 - 94
menu:
// ... lines 96 - 120
-
label: 'Important stuff'
icon: 'exclamation'
children:
-
label: 'Silly kittens'
url: 'https://www.youtube.com/results?search_query=silly+kittens'
target: '_blank'
-
label: 'Wet cats'
url: 'http://www.boredpanda.com/funny-wet-cats/'
target: '_blank'
// ... lines 133 - 208

Yep, instead of entity or route keys, you can skip all of that and just add url. And of course, you can set the target on any item.

Re-organizing the Config

Ok team, our admin menu is complete! The last thing I want to show you isn't anything special to this bundle: it's just a nice way to organize any configuration. In fact, this trick will become the standard way to organize things in Symfony 4.

Right now, well, our admin configuration goes from line 81 of config.yml to line

  1. Wow! It's huge!

To clear things up, I'd like to create a new file called admin.yml. Copy all of this config, remove it, and add it to admin.yml:

128 lines | app/config/admin.yml
easy_admin:
site_name: 'Aqua<i>Note</i>'
design:
brand_color: '#81b9ba'
assets:
css: ['css/custom_backend.css']
js:
- 'https://unpkg.com/snarkdown@1.2.2/dist/snarkdown.umd.js'
- 'js/custom_backend.js'
templates:
field_id: 'admin/fields/_id.html.twig'
form_theme:
- horizontal
- easy_admin/_form_theme.html.twig
menu:
- { label: 'Dashboard', route: 'admin_dashboard', default: true }
- { label: 'Open front-end', route: 'app_genus_list' }
- { entity: 'User', label: 'Users', icon: 'user' }
-
label: 'Genus'
children:
-
entity: 'Genus'
label: 'Pet genus'
icon: 'paw'
params:
action: 'show'
id: 2
- Genus
-
entity: 'Genus'
label: 'Genuses (sorted by ID)'
params:
sortField: 'id'
sortDirection: 'ASC'
pizza: 'delicious'
- GenusHorde
- { label: 'Related' }
- { entity: 'GenusNote', label: 'Notes', icon: 'sticky-note' }
- { entity: 'SubFamily', label: 'Sub-Families', icon: '' }
-
label: 'Important stuff'
icon: 'exclamation'
children:
-
label: 'Silly kittens'
url: 'https://www.youtube.com/results?search_query=silly+kittens'
target: '_blank'
-
label: 'Wet cats'
url: 'http://www.boredpanda.com/funny-wet-cats/'
target: '_blank'
list:
title: 'List of %%entity_label%%'
actions: ['show', 'export']
entities:
Genus:
class: AppBundle\Entity\Genus
controller: AppBundle\Controller\EasyAdmin\GenusController
label: Genuses
help: Genuses are not covered under warranty!
list:
help: Do not feed!
actions:
- { name: 'edit', icon: 'pencil', label: 'Edit' }
- { name: 'show', icon: 'info-circle', label: '' }
fields:
- 'id'
- 'name'
- 'isPublished'
- { property: 'firstDiscoveredAt', format: 'M Y', label: 'Discovered' }
- 'funFact'
- { property: 'speciesCount', format: '%b' }
sort: 'name'
search:
help: null
fields: ['id', 'name']
show:
actions:
-
name: 'genus_feed'
type: 'route'
label: 'Feed genus'
css_class: 'btn btn-info'
icon: 'cutlery'
- { name: 'changePublishedStatus', css_class: 'btn' }
# templates:
# field_id: 'admin/fields/_id.html.twig'
form:
fields:
- { type: 'group', css_class: 'col-sm-6', label: 'Basic information' }
- name
- speciesCount
- { property: 'firstDiscoveredAt', type_options: { widget: 'single_text' }}
- { property: 'subFamily', type: 'easyadmin_autocomplete' }
- { type: 'section', label: 'Optional' }
- { property: 'funFact', type: 'textarea', css_class: 'js-markdown-input' }
- isPublished
- { type: 'group', css_class: 'col-sm-6', label: 'Studied by ...' }
-
property: 'genusScientists'
type: 'text'
type_options:
mapped: false
attr: { class: 'js-genus-scientists-field' }
-
type: 'group'
css_class: 'col-sm-6 new-row'
label: 'Identification'
icon: 'id-card-o'
help: 'For administrators'
-
property: id
type_options: {disabled: true}
-
property: 'slug'
help: 'unique auto-generated value'
type_options: { disabled: true }
new:
fields:
- '-id'
- '-slug'
GenusHorde:
class: AppBundle\Entity\Genus
label: HORDE of Genuses

Perfect!

Now, we just need to make sure that Symfony loads this file. At the top of config.yml, just load another resource: admin.yml:

81 lines | app/config/config.yml
imports:
// ... lines 2 - 4
- { resource: admin.yml }
// ... lines 6 - 81

And that is it! When we refresh, everything still works!

Phew, we're done! EasyAdminBundle is great. But of course, depending on how custom you need things, you might end up overriding a lot of different parts. Many things can be done via configuration. But by using the tools that we've talked about, you can really override everything. Ultimately, customizing things will still be a lot faster than building all of this on your own.

All right guys, thank you so much for joining me! And a huge thanks to my co-author Andrew for doing all the actual hard work.

Ok, seeya next time!