idavoll/menu-module
A Laravel menu module for defining, organizing, and rendering application navigation. It helps PHP/Laravel projects manage menu structures and keep navigation configuration maintainable.
Treat this as an AsgardCMS module, not a standalone Composer library. It will not boot without AsgardCMS's module loader, its Menu entity migrations, and the admin theme in place.
composer.json for asgardcms/* packages and confirm which Laravel version is pinned — this module predates Laravel 6/7 conventions and will not drop into a modern app.composer require idavoll/menu-module
php artisan module:list # confirm "Menu" is registered
php artisan module:migrate Menu
The module lives in Modules/Menu/. That directory is your primary reference.Modules/Menu/Entities/Menu.php and MenuItem.php — the actual schema contract (fields like name, title, position, link_type, page_id, uri, target, class).Modules/Menu/Repositories/* — how menus are queried and what eager-loads exist (watch for N+1 on nested items).Modules/Menu/Http/Controllers/ and Routes/ — the admin surface and backend route names.Modules/Menu/Resources/views/ — the Blade partial(s) you will end up overriding for theming./backend/menu/menus or the route from Routes/), create a menu with machine name main.{!! Menu::render('main') !!} or a @menu('main') directive).Admin-managed menus + theme render. The intended workflow is non-developers manage menus in /backend, and your Blade layout renders them by machine name. Keep your theme templates calling a single helper so you can swap the renderer later.
Two link strategies. Menu items generally support a polymorphic "Page" link (page_id → Page module) or a raw URI/URL. Prefer page links — they survive slug changes; raw URLs are for external links and are the source of most stale-menu bugs after a site restructure.
Nested/tree items. Items carry parent_id and a positional ordering column. When rendering manually (rather than via the module's own view), build the tree in one query and group by parent in PHP rather than recursing per item:
$items = MenuItem::where('menu_id', $menu->id)
->where('status', 1)
->orderBy('position')
->get()
->groupBy('parent_id'); // then recurse in the view via $items[0], $items[$parentId]
Multi-language aware links. AsgardCMS is multilingual; if your site is, resolve menu item URLs through the CMS's locale-aware URL resolver rather than concatenating uri strings by hand, or language-switched pages will link back to the default locale.
Seeding menus for a theme. For repeatable deploys, create menus and items in a module seeder (Modules/Menu/Database/Seeders/) using the repositories, keying items by a stable machine name so re-seeding is idempotent (updateOrCreate semantics on the menu name).
Cache the rendered output, not the query. Rendered menus are read-mostly. Cache the final HTML per (menu, locale) key and bust it in the menu/item model's saved/deleted events — cheaper and simpler than patching every query path.
View overriding. Because this is a CMS module, override the module's Blade partials from your theme/module rather than editing Modules/Menu/Resources/views directly; that keeps upgrades (if any) viable.
NOASSERTION. Composer reports no recognized license. Before shipping commercially, read the LICENSE file in the repo and get legal sign-off — do not assume MIT because it's Laravel-adjacent.Modules/Menu/) rather than depending on Packagist, so a repo takedown doesn't break installs.status/enabled flags, menu_id FK correctness, and position being set (null positions sort unpredictably or drop out of orderBy).uri items before any permalink migration.{!! !!} deliberately and make sure titles coming from the CMS are escaped inside the partial to avoid stored-XSS from admin input.php artisan route:list | grep menu — backend and frontend menu routes can collide with your own menu route name; namespace yours.Menu entity for adding fields (e.g. an icon column), the module's facade for swapping the renderer, and the repository interfaces for custom queries. If you need behavior beyond these, you are editing vendor code — fork first.How can I help you explore Laravel packages today?