If you liked what you've learned so far, dive in!
Subscribe to get access to this tutorial plus
video, code and script downloads.
With a Subscription, click any sentence in the script to jump to that part of the video!
Login SubscribeOur menu on the left is getting a little long and... kind of confusing... since we now have two question links. To make this more user-friendly, let's divide this into a sub-menu. We do that inside of DashboardController
... because that's, of course, where we configure the menu items.
To create a sub-menu, say yield MenuItem::subMenu()
and then give that a name - like Questions
- and an icon... just like we do with normal menu items.
To populate the items in this menu, say ->setSubItems()
, pass this an array, and then we'll wrap our other two menu item objects inside of this. Of course, now we need to indent, remove the yield
, and... replace the semicolons with commas.
Perfect! Now change Questions
to... how about All
... and let's play with the icons. Change the first to fa fa-list
... and the second to fa fa-warning
.
... lines 1 - 24 | |
class DashboardController extends AbstractDashboardController | |
{ | |
... lines 27 - 57 | |
public function configureMenuItems(): iterable | |
{ | |
... line 60 | |
yield MenuItem::subMenu('Questions', 'fa fa-question-circle') | |
->setSubItems([ | |
MenuItem::linkToCrud('All', 'fa fa-list', Question::class) | |
->setController(QuestionCrudController::class) | |
->setPermission('ROLE_MODERATOR'), | |
MenuItem::linkToCrud('Pending Approval', 'fa fa-warning', Question::class) | |
->setPermission('ROLE_MODERATOR') | |
->setController(QuestionPendingApprovalCrudController::class), | |
]); | |
... lines 70 - 73 | |
} | |
... lines 75 - 134 | |
} |
Let's try that. Move over... refresh and... ahhh, much cleaner!
But wait, there's more we can do with the menu... like adding separators... technically called "sections". Right after linkToDashboard()
, add yield MenuItem::section()
and pass it Content
.
... lines 1 - 57 | |
public function configureMenuItems(): iterable | |
{ | |
... line 60 | |
yield MenuItem::section('Content'); | |
... lines 62 - 75 | |
} | |
... lines 77 - 138 |
Let's put one more down here - yield MenuItem::section()
... but this time leave the label blank. So unlike sub-menus, which wrap menu items, you can just pop a section anywhere that you want a separator.
... lines 1 - 57 | |
public function configureMenuItems(): iterable | |
{ | |
... lines 60 - 72 | |
yield MenuItem::linkToCrud('Users', 'fas fa-users', User::class); | |
yield MenuItem::section(); | |
yield MenuItem::linkToUrl('Homepage', 'fas fa-home', $this->generateUrl('app_homepage')); | |
} | |
... lines 77 - 138 |
Let's go check it out. Refresh and... very nice! Separator one says "Content"... and separator two gives us a little gap without any text.
We saw earlier that you can add menu links to point to a dashboard, other CRUD sections... or just any URL you want, like the Homepage. So, not surprisingly, you can also link to external sites. For instance, let's say that I love StackOverflow so much, that I want to link to it. We can tweak the icons, and for the URL, pass whatever you want, like https://stackoverflow.com.
... lines 1 - 57 | |
public function configureMenuItems(): iterable | |
{ | |
... lines 60 - 75 | |
yield MenuItem::linkToUrl('StackOverflow', 'fab fa-stack-overflow', 'https://stackoverflow.com'); | |
} | |
... lines 78 - 139 |
Oh, but let me fix my icon name. Great! Now when we refresh... no surprise, that works fine.
If you look closer at these menu items, you'll see that they have a lot of options on them! We know we have things like setPermission()
and setController()
, but we also have methods like setLinkTarget()
, setLinkRel()
, setCssClass()
, or setQueryParameter()
. For this case, let's ->setLinkTarget('_blank')
... so that now if I click "StackOverflow", it pops up in a new tab.
... lines 1 - 57 | |
public function configureMenuItems(): iterable | |
{ | |
... lines 60 - 75 | |
yield MenuItem::linkToUrl('StackOverflow', 'fab fa-stack-overflow', 'https://stackoverflow.com') | |
->setLinkTarget('_blank'); | |
} | |
... lines 79 - 140 |
Next: what if we need to disable an action on an entity-by-entity basis? Like, we want only want to allow questions to be deleted if they are not approved. Let's dive into that.
Hey @gverdu!
You should be able to do that by:
A) In your "dashboard" controller, override the configureDashboard()
method.
B) Then, call renderSidebarMinimized()
before returning the object:
public function configureDashboard(): Dashboard
{
return parent::configureDashboard()
->renderSidebarMinimized();
}
Source for the docs for that method are here https://symfony.com/bundles/EasyAdminBundle/current/dashboards.html#dashboard-configuration
I hope that helps! I also think the collapsed sidebar looks nice. Oh, and remember: once you have clicked the minimize/maximize, that is stored in the browser's local storage and will override this "default" setting of collapsed.
Cheers!
I don't see the submenu links unless I ditch ->setController(QuestionCrudController::class)->setPermission('ROLE_MODERATOR') respectively ->setPermission('ROLE_MODERATOR')->setController(QuestionPendingApprovalCrudController::class) portion of the code. Of course that makes those sublinks unfunctional. I tried the code downloaded from symfonicasts site with the same result.
My code is here https://github.com/petre-sy...
Hey Diaconescu,
Most probably your logged in user does not have that ROLE_MODERATOR somehow. Please, try to log out and log in again, then check the Symfony web debug toolbar, in particular, the Security tab there - it will show you roles that your user has. It should be in "Roles" or in "Inherited Roles" row. If the user does not have that ROLE_MODERATOR role somehow - please, add it first and try again (you may need to log our and log in just in case).
I hope this helps!
Cheers!
// 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
}
}
hello, when the site loads, the menu appears expanded, how can I change the code so that it loads collapsed by default?