Installation:
composer require knplabs/knp-menu
No additional configuration is required for basic usage.
First Use Case: Create a simple menu in a controller or service:
use Knp\Menu\MenuItem;
use Knp\Menu\ItemInterface;
$menu = new MenuItem('root');
$menu->addChild('Home', ['route' => 'home']);
$menu->addChild('About', ['route' => 'about']);
return $menu;
Rendering: Use Twig (recommended) or manually render the menu:
{{ knp_menu_render(menu, { 'depth': 2, 'linkAttributes': { 'class': 'nav-link' } }) }}
Key Files:
vendor/knplabs/knp-menu/src/ (Core classes)Resources/views/ (Twig templates, if using Symfony)Dynamic Menu Building: Fetch menu items from a database and build the menu dynamically:
$menu = $factory->createItem('root');
foreach ($categories as $category) {
$item = $menu->addChild($category->name, ['route' => 'category_show', 'parameters' => ['id' => $category->id]]);
if ($category->hasChildren()) {
$item->setAttribute('has_children', true);
}
}
Menu Factories:
Use MenuFactory to create reusable menu structures:
$factory = new MenuFactory();
$menu = $factory->createItem('main_menu');
$menu->addChild('Dashboard', ['route' => 'dashboard']);
$menu->addChild('Settings', ['route' => 'settings']);
Twig Integration: Extend Twig with custom menu rendering:
{% macro render_menu(menu, options) %}
<ul class="menu">
{% for item in menu %}
<li>
{% if item.children|length > 0 %}
<a href="{{ path(item.route, item.parameters) }}">{{ item.label }}</a>
{{ render_menu(item.children, options) }}
{% else %}
<a href="{{ path(item.route, item.parameters) }}">{{ item.label }}</a>
{% endif %}
</li>
{% endfor %}
</ul>
{% endmacro %}
Caching: Cache menus for performance:
$menu = $factory->createItem('cached_menu');
$menu->setCache($cache->getItem('menu_cache_key'));
knp_menu.twig_extension in Twig configurations for seamless rendering.MenuFactory and MenuItem:
$this->app->bind('knp-menu.factory', function () {
return new MenuFactory();
});
$menu = $factory->createItem('route_menu');
foreach ($router->getRouteCollection()->getByName('*') as $route) {
$menu->addChild($route->getName(), ['route' => $route->getName()]);
}
Circular References: Avoid adding the same child to multiple parents, which can cause infinite loops in rendering.
// Bad: Circular reference
$item1->addChild($item2);
$item2->addChild($item1);
Twig Autoescape: Ensure Twig autoescape is disabled for menu rendering to avoid HTML escaping issues:
{{ knp_menu_render(menu, { 'autoescape': false }) }}
Route Parameters:
Incorrect route parameters can lead to broken links. Validate parameters before passing them to addChild:
$item->addChild('Profile', ['route' => 'profile_show', 'parameters' => ['id' => $user->id]]);
Menu Depth: Deeply nested menus can cause performance issues. Limit depth in rendering:
{{ knp_menu_render(menu, { 'depth': 2 }) }}
MenuItem::setCurrentUri() to highlight the current page in the menu:
$menu->setCurrentUri(request()->getUri());
Router to validate:
if ($router->getRouteCollection()->get($routeName)) {
$item->addChild('Link', ['route' => $routeName]);
}
AppServiceProvider:
$twig->addExtension(new \Knp\Menu\Twig\MenuExtension($menuFactory));
setAttribute() and getAttribute() to store custom data, but avoid overusing them for performance reasons.Custom Renderers:
Extend MenuRenderer to create custom output formats (e.g., JSON for APIs):
class JsonMenuRenderer extends MenuRenderer
{
public function render(ItemInterface $item, array $options)
{
return json_encode($item->toArray());
}
}
Menu Builders: Create builder classes to encapsulate complex menu logic:
class AdminMenuBuilder
{
public function build(MenuItem $menu)
{
$menu->addChild('Users', ['route' => 'admin_users']);
$menu->addChild('Settings', ['route' => 'admin_settings']);
}
}
Event Listeners:
Use events (e.g., MenuItem::PRE_BUILD) to modify menus dynamically:
$menu->addListener('PRE_BUILD', function (MenuItem $item) {
if ($item->getName() === 'main_menu') {
$item->addChild('Logout', ['route' => 'logout']);
}
});
How can I help you explore Laravel packages today?