Weave Code
Code Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Menu Bundle Laravel Package

braunstetter/menu-bundle

View on GitHub
Deep Wiki
Context7

Getting Started

Minimal Setup

  1. Installation

    composer require braunstetter/menu-bundle
    

    Register the bundle in config/bundles.php:

    return [
        // ...
        Braunstetter\MenuBundle\MenuBundle::class => ['all' => true],
    ];
    
  2. First Menu Creation Create a menu class (e.g., src/Menu/NavigationMenu.php):

    namespace App\Menu;
    
    use Braunstetter\MenuBundle\Menu\Menu;
    use Braunstetter\MenuBundle\Menu\MenuItem;
    
    class NavigationMenu extends Menu
    {
        public function build(): void
        {
            $this->addItem(new MenuItem('Home', 'home'))
                 ->addChild(new MenuItem('About', 'about'))
                 ->addChild(new MenuItem('Contact', 'contact'));
        }
    }
    
  3. Register the Menu Add it to config/packages/braunstetter_menu.yaml:

    menus:
        navigation: App\Menu\NavigationMenu
    
  4. First Render Use Twig in your template:

    {{ render(controller('braunstetter_menu.controller.menu:render', {'menuName': 'navigation'})) }}
    

First Use Case: Dynamic Menu Items

Extend the menu with dynamic items (e.g., user-specific links):

public function build(): void
{
    $this->addItem(new MenuItem('Home', 'home'));
    if (auth()->check()) {
        $this->addItem(new MenuItem('Dashboard', 'dashboard'));
    }
}

Implementation Patterns

Menu Hierarchy Management

  1. Nested Menus

    $parent = new MenuItem('Products', 'products');
    $parent->addChild(new MenuItem('Laptops', 'laptops'));
    $parent->addChild(new MenuItem('Phones', 'phones'));
    $this->addItem($parent);
    
  2. Conditional Items

    $this->addItem(
        new MenuItem('Admin', 'admin')
            ->setActiveWhen(function () {
                return request()->is('admin/*');
            })
    );
    

Reusable Menu Classes

  • Extend for Breadcrumbs

    class BreadcrumbMenu extends Menu
    {
        public function build(): void
        {
            $this->addItem(new MenuItem('Home', 'home'));
            if (request()->is('products/*')) {
                $this->addItem(new MenuItem('Products', 'products'));
            }
        }
    }
    
  • Share Across Controllers Inject the menu service:

    use Braunstetter\MenuBundle\Menu\MenuManagerInterface;
    
    public function __construct(private MenuManagerInterface $menuManager) {}
    
    public function show()
    {
        $menu = $this->menuManager->getMenu('navigation');
        // Use $menu->getItems() as needed
    }
    

Event-Driven Extensibility

  1. Listen for Menu Events
    // src/EventListener/MenuListener.php
    use Braunstetter\MenuBundle\Event\MenuEvent;
    
    class MenuListener
    {
        public function onMenuBuild(MenuEvent $event)
        {
            if ($event->getMenuName() === 'navigation') {
                $event->getMenu()->addItem(new MenuItem('Custom Item', 'custom'));
            }
        }
    }
    
    Register in services.yaml:
    services:
        App\EventListener\MenuListener:
            tags:
                - { name: kernel.event_listener, event: menu.build, method: onMenuBuild }
    

Integration with Twig

  1. Render Blocks

    {% render_menu 'navigation' %}
    {% render_menu 'navigation' with {'template': 'AppBundle:Menu:custom.html.twig'} %}
    
  2. Custom Templates Override default templates in templates/braunstetter_menu/:

    {# templates/braunstetter_menu/menu.html.twig #}
    <ul>
        {% for item in items %}
            <li class="{{ item.active ? 'active' }}">{{ item.label }}</li>
        {% endfor %}
    </ul>
    

Gotchas and Tips

Common Pitfalls

  1. Caching Issues

    • Menus are cached by default. Clear cache after changes:
      php bin/console cache:clear
      
    • Disable caching in config/packages/braunstetter_menu.yaml:
      menus:
          navigation: App\Menu\NavigationMenu
          cache: false
      
  2. Active Item Logic

    • Ensure setActiveWhen() uses a callable (closure or method):
      // ❌ Wrong (string)
      $item->setActiveWhen('request()->is("home")');
      
      // ✅ Correct (closure)
      $item->setActiveWhen(fn() => request()->is("home"));
      
  3. Circular Dependencies

    • Avoid adding the same menu as a child of itself (e.g., recursive breadcrumbs).

Debugging Tips

  1. Dump Menu Structure

    $menu = $this->menuManager->getMenu('navigation');
    dump($menu->getItems()); // Inspect raw data
    
  2. Check Event Firing

    • Verify listeners are registered:
      php bin/console debug:event-dispatcher
      
  3. Template Overrides

    • Ensure custom templates are in the correct directory (templates/braunstetter_menu/).

Extension Points

  1. Custom Matchers Extend Braunstetter\MenuBundle\Matcher\MatcherInterface for unique active-item logic:

    class CustomMatcher implements MatcherInterface
    {
        public function isActive(MenuItem $item): bool
        {
            return request()->user()?->isAdmin();
        }
    }
    

    Register in services.yaml:

    services:
        App\Matcher\CustomMatcher:
            tags:
                - { name: braunstetter_menu.matcher }
    
  2. Dynamic Menu Loading Use MenuBuilderInterface to load menus dynamically:

    $menu = $this->menuManager->getMenu('navigation', ['dynamic' => true]);
    
  3. Localization Pass translations to menu items:

    $this->addItem(new MenuItem($this->translator->trans('menu.home'), 'home'));
    

Performance Considerations

  • Lazy Loading: Defer menu building until first use by implementing LazyBuildableMenuInterface.
  • Fragment Caching: Cache rendered menu fragments in Twig:
    {% cache app.menu_navigation (app.user) %}
        {{ render_menu('navigation') }}
    {% endcache %}
    
Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
emuniq/filament-browser-notifications
syriable/filament-translator
hungnm28/livewire-form
wenprise/eloquent
crudly/encrypted
fadion/bouncy
cuci/prototurk-sdk
gos/pubsub-router-bundle
cuci/prototurk-sdk-symfony
clementtalleu/easyadmin-markdown-bundle
codeflextech/permission-manager
karnoweb/livewire-datepicker
sayedenam/sayed-dashboard
milito/query-filter
apiboxsym/user-bundle
apiboxsym/health-check-bundle
jayeshmepani/jpl-moshier-ephemeris-php
elnasnato/laraliveui
labrodev/rest-sdk
sampaui/sampaui