aropixel/menu-bundle
A Symfony bundle that extends Aropixel Admin Bundle to manage your website's menus (main menu, footer, and more), with routes, migrations, and support for six languages. Requires Symfony 4 and the admin bundle.
Requirements (v3.0):
aropixel/admin-bundle ^3.0 (mandatory — 3.0 is a rewrite on top of it)Installation:
composer require aropixel/menu-bundle:^3.0
Follow doc/installation.md in the v3.0.0 tag — the 2.x install steps (separate yaml, route include, manual migration order) no longer match the rewritten setup.
Upgrading from 2.x — read this first:
There is no in-place upgrade path. Do not run composer update aropixel/menu-bundle on an existing 2.x install expecting it to work. Read UPGRADE-3.0.md and the installation guide before touching production. Treat 3.0 as a fresh integration.
Configuration:
Menus are still defined in config with per-menu depth, but the editor is now embedded in the AdminBundle 3.0 UI. Register menus there rather than in the old standalone admin route:
aropixel_menu:
menus:
main:
name: "Main Menu"
depth: 3
First use case:
Open the menu editor from within the AdminBundle 3.0 admin (no separate /admin/menu route as in 2.x). Build the main menu via the configurable, drag-and-drop editor, then render it on the front end with the helpers in doc/front-end.md.
Admin management:
depth per menu in yaml; the editor respects those settings.doc/sources.md) for custom content.Front-end rendering:
The v3 rendering helpers live in doc/front-end.md — use those rather than the 2.x Twig idioms, which changed with the rewrite. Typical pattern:
<nav>
{% for item in get_menu('main') %}
<a href="{{ item|get_link }}">{{ item.title }}</a>
{% endfor %}
</nav>
Verify exact function/filter names against doc/front-end.md for 3.0.0 before porting 2.x templates.
Custom item sources (v3 pluggable sources):
Custom sources are a first-class concept in 3.0 — register them per doc/sources.md.
class ProductMenuSource implements MenuSourceInterface {
public function getAvailableItems(array $menuItems): array {
return $this->productRepository->findAll()->map(fn($p) => [
'value' => $p->getId(),
'label' => $p->getName(),
'type' => 'product',
]);
}
// ... remaining required methods per 3.0 interface
}
Re-verify the interface signature for 3.0 — it is part of the rewrite and should not be assumed identical to 2.x.
Custom entity integration:
Extend the base Menu entity to add fields, then point config at it:
#[ORM\Entity]
class Menu extends BaseMenu {
#[ORM\Column(type: 'boolean')]
private bool $newTab = false;
}
aropixel_menu:
entity: App\Entity\Menu
Confirm the config key still exists in 3.0 before relying on it (see doc/installation.md).
Nested menus:
Set depth per menu; iterate children recursively in Twig.
{% for item in get_menu('main') %}
<li>
<a href="{{ item|get_link }}">{{ item.title }}</a>
{% if item.children|length > 0 %}
<ul>{% include 'menu/_nested.html.twig' with {'items': item.children} %}</ul>
{% endif %}
</li>
{% endfor %}
No in-place upgrade:
The single biggest 3.0 trap. composer update on a 2.x project will break things. Follow UPGRADE-3.0.md and plan a migration, not an upgrade.
AdminBundle 3.0 is a hard requirement: 3.0 is built on AdminBundle ^3.0. If your app is pinned to AdminBundle 2.x, you cannot install MenuBundle 3.0. Upgrade AdminBundle first.
Symfony / Doctrine version gates: Symfony 6.4+, 7.x or 8.x, Doctrine ORM 2 or 3. Older Symfony or ORM 1.x projects must upgrade before adopting 3.0.
Editor location changed:
The menu editor is now inside the AdminBundle UI, not a standalone /admin/menu route. If a 2.x link, bookmark or test hits the old route, update it.
Route / config includes from 2.x:
config/routes.yaml includes and the aropixel_menu.yaml structure should be re-checked against doc/installation.md — the rewrite may change which files are loaded.
Source interface changes:
Custom sources written for 2.x may not compile against 3.0. Re-implement against the 3.0 interface documented in doc/sources.md.
Front-end helpers changed:
Twig function/filter names and signatures for rendering may differ. Port templates against doc/front-end.md and test each one.
Caching: Menus may appear stale after edits; clear the cache:
php bin/console cache:clear
Locate registered sources: Inspect the source chain service from a dev console or a debug template after confirming the 3.0 service ID.
{{ dump(app.container.get('aropixel_menu.source_chain').getSources()) }}
Log resolved menu items: Decorate / extend the menu handler and log output:
class CustomMenuHandler extends MenuHandler {
public function getMenuItems(string $menuCode): array {
$items = parent::getMenuItems($menuCode);
\Log::info('Menu items for '.$menuCode, ['items' => $items]);
return $items;
}
}
Verify link resolution:
{{ item|get_link|dump }}
Schema check:
If the new migrations fail, inspect the menu table — the nested-set columns (lft, rgt, parent_id) and types should match what the 3.0 migrations expect.
Custom menu handlers:
Extend MenuHandler to filter or transform items, and register as a service (tag name may differ in 3.0 — verify).
class CustomMenuHandler extends MenuHandler {
public function getMenuItems(string $menuCode): array {
return array_filter(
parent::getMenuItems($menuCode),
fn($item) => $item->isActive()
);
}
}
Override templates:
Customize the editor/admin look by overriding AdminBundle 3.0 templates (path may have changed from templates/AropixelMenuBundle/).
Dynamic menu codes: Drive menu definitions from services/parameters:
aropixel_menu:
menus: '%menu_codes%'
Validation on items:
Add constraints on your extended Menu entity:
#[Assert\Callback]
public function validate(ExecutionContextInterface $context): void {
if ($this->type === 'page' && empty($this->link)) {
$context->buildViolation('Page link is required')->addViolation();
}
}
Performance:
parent_id and type.Do a dry-run on a throwaway branch: Because there's no in-place upgrade, stand up a fresh 3.0 install on a branch, wire the config and sources, confirm the front end matches, then migrate.
Multi-language menus:
Add translated title fields on your extended entity and filter per locale in getAvailableItems().
Preview images:
Add a previewUrl field to the Menu entity and render it in the editor template.
API exposure:
#[Route('/api/menus/{code}', name: 'api_menu_get', methods: ['GET'])]
public function getMenu(string $code, MenuHandler $handler): JsonResponse {
return new JsonResponse($handler->getMenuItems($code));
}
Testing: Assert structure via the handler in PHPUnit:
public function testMainMenuStructure(): void {
$handler = $this->container->get(MenuHandler::class);
$menu = $handler->getMenuItems('main');
$this->assertCount(3, $menu);
}
Keep the doc links handy:
doc/installation.md, doc/sources.md, doc/front-end.md, and UPGRADE-3.0.md in the v3.0.0 tag are the authoritative source for 3.0 — the 2.x docs online may be stale.
How can I help you explore Laravel packages today?