idci/nav-bar-bundle
A Symfony bundle that adds a configurable navigation bar to your application, letting you define menus, links, and routes in configuration and render them with Twig. Useful for centralizing site navigation and keeping templates free of hardcoded menu markup.
Before anything else, calibrate: idci/nav-bar-bundle is a Symfony2 bundle, not a Laravel package. There is no service provider, no auto-discovery, no Blade, no config/nav-bar.php merge. A composer require in a Laravel app will either fail dependency resolution (Symfony 2.x constraints vs. Laravel's symfony/* ^6/^7) or drag in an unusable framework generation. Treat the package as a reference implementation to read, not a dependency to install.
Minimal steps if you're evaluating it:
git clone https://github.com/IDCI-Consulting/NavBarBundle /tmp/navbar
Resources/config/services.xml (or .yml) — service IDs, builder/manager class, DI tags.DependencyInjection/IDCINavBarExtension.php — configuration tree, what's actually configurable.Menu/Item/Builder model classes — the data shape you'll want to copy.Resources/views/*.twig — the recursive render and active-state logic.app/AppKernel.php:
new IDCI\Bundle\NavBarBundle\IDCINavBarBundle(),
and import the bundle's routing/services per its Resources/config/. Expect to --ignore-platform-reqs or pin PHP ≤7.x.First use case on a Laravel stack: a two-hour spike where you open the bundle, sketch its item model (label, route/URL, children, active flag, optional role gate), and close the evaluation. The deliverable is a data-shape note, not an integration.
The realistic day-to-day pattern is "borrow the model, build the component." The bundle's value is its menu-tree shape and active-state logic; both port to Laravel in an afternoon.
1. Mirror the item model as value objects.
final class MenuItem
{
public function __construct(
public string $label,
public string $route, // route name, not URL
public array $params = [],
public array $children = [],
public ?string $gate = null, // permission gate, checked at render
public ?string $icon = null,
) {}
}
Keep it immutable and build the tree once (in a MenuBuilder service or a config/navigation.php), not per-request ad hoc.
2. Recursive Blade component instead of the Twig recursive include.
{{-- resources/views/components/nav/menu.blade.php --}}
<ul {{ $attributes->merge(['class' => 'nav']) }}>
@foreach ($items as $item)
@continue($item->gate && ! auth()->user()?->can($item->gate))
<li>
<a href="{{ route($item->route, $item->params) }}"
@class(['active' => request()->routeIs($item->route)])
@if($item->children) aria-haspopup="true" aria-expanded="false" @endif>
{{ $item->label }}
</a>
@if($item->children)
<x-nav.menu :items="$item->children" class="submenu" />
@endif
</li>
@endforeach
</ul>
This is where the Symfony bundle stops helping: it renders markup only, so ARIA, keyboard traversal, and mobile disclosure behavior remain your work.
3. Active-state resolution. The bundle compares the Symfony RequestStack against the item's route. The Laravel equivalent is request()->routeIs($item->route). Note: routeIs supports wildcards (admin.*) — use them for section-level highlighting instead of hand-rolling prefix matching.
4. Inject the menu, don't fetch it in the layout. Use a view composer so the layout stays dumb:
View::composer('layouts.app', fn ($view) =>
$view->with('nav', app(MenuBuilder::class)->build())
);
5. Authorization at render time, not build time. The bundle's role-gating (if present) resolves during build; porting that to Laravel means evaluating Gate::allows() in the component so the menu reflects session state without rebuilding the tree.
6. Config vs. database — decide explicitly. Static config/navigation.php covers most navs and is cache-friendly. Only reach for a menus/menu_items table if non-engineers must edit menus; then cache the tree and invalidate on write. The bundle implies the dynamic path but doesn't solve CMS permissions or ordering UX — that's a product decision, not a library one.
7. Integration tip — one nav, one component. Replace bespoke nav markup in the layout in a single pass. Mixed hand-written <ul>s and the component drift on active-state handling within a sprint.
symfony/*: ~2.3; Laravel needs ^6/^7. Composer will resolve-fail or you force it and inherit an unmaintained fork. If you see "conflict with symfony/http-foundation" in CI, that's this, and it isn't fixable with flags.Resources/config/services.xml before the README. Service IDs and the builder class live there; docs are frequently out of sync with code on dead bundles.{{ path('route') }} becomes route(), is_granted() becomes @can, and Symfony's app.request becomes request(). The recursive include ({% include ... with {items: child} %}) maps to <x-nav.menu :items="..." />, but escaping and attribute merging differ.render() method per item type) in your own component rather than adapting its interfaces.KnpMenuBundle. It's the maintained equivalent and worth naming in the ADR so nobody re-opens this evaluation.How can I help you explore Laravel packages today?