zetta/menu-bundle
Zetta Menu Bundle is a PHP package for building and managing hierarchical navigation menus. It offers menu and menu-item models, tree operations, and integration points for Symfony applications, helping developers define, organize, and render multi-level menus with less boilerplate.
Verdict: Do not adopt. zetta/menu-bundle is not a Laravel package — it is a Symfony bundle (namespace Zetta\MenuBundle), released once in October 2015 and never touched since. At 9 stars, 0 dependents, and a composite score of 0.075, this is effectively abandoned software that fails the minimum bar for a production dependency.
Architecture fit — poor. The package is built around Symfony's runtime contracts: the DI container, the EventDispatcher, the Security component's authorization checker, and almost certainly KnpMenu/KnpMenuBundle for tree construction plus Twig for rendering. Laravel has none of these; it has the service container, the event dispatcher, Gate/Policy authorization, and Blade. Every integration point would need a shim. The only transferable asset is the idea — decorating menu items with a required role/permission and filtering the tree at render time via a voter — not the code.
Integration feasibility — not feasible as-is. A 2015 Symfony bundle will declare constraints against Symfony 2.x and PHP 5.x. Modern Composer resolution on PHP 8.2+ with Laravel 10/11 will fail on platform requirements at best; if forced through with --ignore-platform-reqs, it will fatal on removed Symfony APIs (ContainerAware, Controller, getRequest() semantics, security.context, Twig 1.x helpers). There is also no Laravel package discovery, no facade, no config publishing.
Technical risk — high and asymmetric. The upside is a small menu-filtering utility we could write ourselves in a day or two; the downside is an unpatched, unmaintained dependency in the authorization path of our UI. Security-critical code from 2015 has had zero CVE review since. A particularly dangerous failure mode: teams often treat a "secure menu" as the access control. Hiding a menu item is presentation, not authorization — if the underlying routes are not independently guarded, we ship an access-control illusion. That risk is inherent to the package's framing, not just its age.
Key questions for the spike:
KnpMenuBundle, and which versions?Stack fit — none, by construction. We are standardizing on Laravel. This artifact targets a different framework, a different template engine, and a PHP runtime that reached end of life years ago. Recommendation is to re-implement the concept natively, treating the repository as a design reference only.
Migration path (concept port, not code port):
MenuBuilder/MenuRenderer service in Laravel: nodes as value objects (label, route, ability, children), filtering delegated to Gate::allows() / $user->can().@can-style guard at the node level.authorize() call in the controller — the menu is cosmetic; the policy is the control. Add a test that asserts every ability declared on a menu node is also enforced on the corresponding route.Compatibility. If any legacy Symfony 2/3 service still needs this bundle, keep it pinned in that application only; do not attempt to bridge it into Laravel. Cross-framework menu sharing via a shared bundle is not worth the coupling.
Sequencing:
Maintenance. Zero upstream maintenance; the maintainer's last commit is roughly a decade old. Any fix is ours to carry on a permanent fork, which means we own the entire surface for a component we could have written in two days.
Support. No issue tracker activity, no documentation beyond a README, no community. On-call engineers will be reverse-engineering PHP 5-era Symfony code during an incident. That is the worst possible shape for a dependency in a user-facing authorization path.
Scaling. Technically trivial — menu trees are built in memory per request, O(nodes), negligible. The real scaling concern is organizational: every new app that "just reuses the bundle" inherits the fork, the platform hacks, and the hidden-vs-forbidden ambiguity.
Failure modes to watch:
Ramp-up. Conceptually near-zero — the domain is a filtered tree. But ramping a developer onto this codebase (Symfony 2 DI XML, Twig 1, security voters) is a multi-day cost for negative long-term value. Native Laravel implementation is familiar to the whole team on day one.
Bottom line for the portfolio: decline adoption, record the decision, and fund a ~3-developer-day native replacement. The only scenario that changes this recommendation is discovering an existing production Symfony 2 application that already depends on the bundle — in which case the work becomes a scoped removal plan, not an integration.
How can I help you explore Laravel packages today?