Installation
Add the bundle to your composer.json:
composer require devilcius/admin-menu-bundle
Enable it in config/bundles.php:
return [
// ...
devilcius\AdminMenuBundle\AdminMenuBundle::class => ['all' => true],
];
Basic Configuration
Ensure SonataAdminBundle is installed and configured. The bundle extends Sonata’s menu system via KnpMenu.
First Use Case Create a listener to modify the default menu. Example:
php bin/console make:listener AdminMenuListener
Update the generated listener to extend the menu:
use Knp\Menu\ItemInterface;
use devilcius\AdminMenuBundle\Event\AdminMenuEvent;
public function onAdminMenu(AdminMenuEvent $event)
{
$menu = $event->getMenu();
$menu->addChild('Custom Menu', ['uri' => $this->router->generate('home')]);
}
Register the listener in config/services.yaml:
services:
App\EventListener\AdminMenuListener:
tags:
- { name: 'kernel.event_listener', event: 'devilcius.admin_menu', method: 'onAdminMenu' }
Dynamic Menu Generation
Use the AdminMenuEvent to dynamically build menus based on user roles, permissions, or runtime conditions:
public function onAdminMenu(AdminMenuEvent $event)
{
$menu = $event->getMenu();
$user = $this->security->getUser();
if ($user->hasRole('ROLE_AUDITOR')) {
$menu->addChild('Audit Logs', ['route' => 'audit_logs']);
}
}
Integration with Sonata Admin Groups Extend existing Sonata admin groups or create new ones:
$menu['System']->addChild('Users', ['route' => 'sonata_admin', 'routeParameters' => ['_controller' => 'sonata.admin.user']]);
Nested Menus
Build hierarchical menus using addChild() with submenus:
$settingsMenu = $menu->addChild('Settings');
$settingsMenu->addChild('Profile', ['route' => 'profile_edit']);
$settingsMenu->addChild('Preferences', ['route' => 'preferences']);
Conditional Rendering Hide/show menu items based on environment or config:
if ($this->container->getParameter('app.debug')) {
$menu->addChild('Debug Tools', ['route' => 'debug_toolbar']);
}
router->generate() for dynamic routes to avoid hardcoding URIs.translationDomain for i18n support:
$menu->addChild('Dashboard', ['route' => 'dashboard'], ['translationDomain' => 'messages']);
setExtra() for icons (requires frontend integration):
$menu->addChild('Reports')->setExtra('icon', '<i class="fa fa-file-pdf"></i>');
sonata_admin for Sonata-generated links:
$menu->addChild('Products', ['route' => 'sonata_admin', 'routeParameters' => ['_controller' => 'sonata.admin.product']]);
Event Name Mismatch
Ensure the listener is tagged with the correct event (devilcius.admin_menu). A typo here will silently fail.
KnpMenu Version Conflicts
The bundle relies on KnpMenu v2. Verify compatibility with your knp/knp-menu-bundle version (e.g., ^2.0). Downgrade if needed:
composer require knp/knp-menu-bundle:2.0
Service Container Access Avoid direct container access in listeners. Inject dependencies via constructor:
// ❌ Anti-pattern
$this->router = $this->container->get('router');
// ✅ Preferred
public function __construct(RouterInterface $router) {
$this->router = $router;
}
Menu Ordering
KnpMenu renders items in registration order. Use setPosition() to control order:
$menu->addChild('First')->setPosition(10);
$menu->addChild('Second')->setPosition(20);
Sonata Admin Route Parameters
Sonata’s admin routes require _controller parameters. Omitting them may break links:
// ❌ Fails
['route' => 'sonata_admin']
// ✅ Works
['route' => 'sonata_admin', 'routeParameters' => ['_controller' => 'sonata.admin.product']]
public function onAdminMenu(AdminMenuEvent $event) {
dump($event->getMenu()->tree());
}
dump() in the bundle’s event subscriber (located in devilcius/AdminMenuBundle/EventListener/AdminMenuListener.php).Custom Menu Builders Create reusable menu builders by encapsulating logic in services:
class AuditMenuBuilder {
public function build(ItemInterface $menu) {
$menu->addChild('Audit Logs', ['route' => 'audit_logs']);
}
}
Then call it from your listener:
$builder = $this->container->get('app.audit_menu_builder');
$builder->build($menu);
Database-Driven Menus Fetch menu items from a database and dynamically build the menu:
$menuItems = $this->menuRepository->findByUser($user);
foreach ($menuItems as $item) {
$menu->addChild($item->getLabel(), ['route' => $item->getRoute()]);
}
Override Default Menu Disable Sonata’s default menu by clearing its items and rebuilding:
$menu->clearChildren();
$menu->addChild('Custom Root', ['uri' => '#']);
Frontend Integration
Use KnpMenu’s setAttribute() to pass data to Twig templates:
$menu->addChild('Dashboard')->setAttribute('class', 'menu-item-active');
Then style it in your template:
<li class="{{ menu.item.attributes.class }}">{{ menu.label }}</li>
How can I help you explore Laravel packages today?