Installation
composer require appaydin/pd-menu
Create a Basic Menu Class
Extend Pd\MenuBundle\Builder\Menu and override createMenu():
// src/Menu/MyMenu.php
namespace App\Menu;
use Pd\MenuBundle\Builder\Menu;
class MyMenu extends Menu {
public function createMenu(array $options = []): ItemInterface {
return $this->createRoot('my_menu')
->addChild('home', 1)
->setRoute('home')
->setLabel('Home');
}
}
Render in Twig
{{ pd_menu_render('App\Menu\MyMenu') }}
Dynamic Admin Sidebar
// src/Menu/AdminMenu.php
class AdminMenu extends Menu {
public function createMenu(array $options = []): ItemInterface {
$menu = $this->createRoot('admin_menu')
->setChildAttr(['class' => 'sidebar']);
$menu->addChild('dashboard', 1)
->setRoute('admin_dashboard')
->setIcon('<i class="fas fa-tachometer-alt"></i>');
$menu->addChild('users', 2)
->setRoute('admin_users')
->setRoles(['ROLE_ADMIN']);
return $menu;
}
}
{# templates/admin/base.html.twig #}
<div class="sidebar-menu">
{{ pd_menu_render('App\Menu\AdminMenu', {
'currentClass': 'active',
'iconTemplate': '<i class="%icon%"></i>'
}) }}
</div>
Menu Hierarchy Construction
// Nested menu with weights
$menu = $this->createRoot('main_menu')
->addChild('products', 1)
->addChild('list', 1)
->setRoute('products_index')
->addChild('create', 2)
->setRoute('products_new')
->addChild('orders', 3)
->setRoute('orders_index');
Dynamic Menu Items via Events
// src/EventListener/DynamicMenuListener.php
class DynamicMenuListener {
public function onMenuBuild(PdMenuEvent $event) {
if ($event->getMenuName() === 'admin_menu') {
$event->getMenu()
->addChild('notifications', 4)
->setRoute('admin_notifications')
->setBadgeCount($this->notificationService->unreadCount());
}
}
}
# config/services.yaml
App\EventListener\DynamicMenuListener:
tags:
- { name: kernel.event_listener, event: admin_menu.event, method: onMenuBuild }
Role-Based Visibility
$menu->addChild('admin', 1)
->setRoute('admin_dashboard')
->setRoles(['ROLE_ADMIN', 'ROLE_SUPER_ADMIN']);
Template Customization
{# Custom template for dropdown menus #}
{% extends '@PdMenu/Default/menu.html.twig' %}
{% block menu_item %}
<li class="dropdown">
<a href="{{ item.route.path }}">{{ item.label }}</a>
{% if item.children|length > 0 %}
<ul class="dropdown-menu">
{% for child in item.children %}
<li>{{ pd_menu_render_child(child) }}</li>
{% endfor %}
</ul>
{% endif %}
</li>
{% endblock %}
Symfony Router Integration
$menu->addChild('profile')
->setRoute('app_profile_show')
->setLinkAttr(['title' => 'User Profile']);
Translation Support
$menu->addChild('settings')
->setLabel('menu.settings') // Translatable key
->setTransDomain('admin');
{{ pd_menu_render('App\Menu\AdminMenu', {'trans_domain': 'admin'}) }}
Menu Caching
# config/packages/pd_menu.yaml
pd_menu:
cache: true
cache_lifetime: 3600
Menu in Controllers
public function index(MenuService $menuService) {
$menu = $menuService->getMenu('main_menu');
return $this->render('homepage.html.twig', ['menu' => $menu]);
}
Event Naming
{menu_class_name}.event (e.g., AdminMenu.event).Route Resolution
config/routes.yaml.'admin_dashboard' vs 'admin/dashboard').dump($menu->getItem('home')->getRoute()) to inspect.Child Parentage
addChildParent() requires the parent item to exist.$menu->addChild('parent', 1)
->addChildParent('child', 2) // Correct
Twig Template Paths
@PdMenu/Default/menu.html.twig.{{ pd_menu_render('App\Menu\MyMenu', {'template': '@MyBundle/menu/custom.html.twig'}) }}
Inspect Menu Structure
$menu = $menuService->getMenu('main_menu');
dump($menu->toArray()); // View raw structure
Event Debugging
public function onMenuBuild(PdMenuEvent $event) {
dump($event->getMenuName(), $event->getOptions());
}
Template Debugging
{{ dump(pd_menu_render('App\Menu\MyMenu', {'debug': true})) }}
Menu Factories
// src/Service/MenuFactory.php
class MenuFactory {
public function createAdminMenu(): ItemInterface {
return (new AdminMenu())->createMenu();
}
}
Menu Versioning
// src/Menu/AdminMenuV2.php
class AdminMenuV2 extends Menu {
public function createMenu(array $options = []): ItemInterface {
// Updated structure
}
}
{{ pd_menu_render('App\Menu\AdminMenuV2') }}
Menu Data Providers
// src/Service/UserMenuProvider.php
class UserMenuProvider {
public function getUserMenu(User $user): ItemInterface {
$menu = (new UserMenu())->createMenu();
$menu->addChild('profile')
->setRoute('user_profile', ['id' => $user->id]);
return $menu;
}
}
Global vs. Local Overrides
{{ pd_menu_render('App\Menu\MyMenu', {'template': '@MyBundle/menu.html.twig'}) }}
pd_menu.yaml only applies to default rendering.Depth Limitation
depth: null renders all levels.depth: 1 renders only top-level items.Icon Template
%icon% placeholder in iconTemplate:
pd_menu:
iconTemplate: '<i class="%icon% fa-fw"></i>'
ArrayAccess Compatibility
ArrayAccess, so you can use:
$menu['home']->setActive(true);
How can I help you explore Laravel packages today?