Меню-билдер в лучших традициях симфони лучше объявить как сервис.
Создаем сервис app.knp.sidebar_menu в файле config/packages/knp_menu.yaml:
Содержимое config/packages/knp_menu.yaml:
knp_menu: twig: template: custom_knp_menu.html.twig parameters: knp_menu.renderer.twig.options: currentClass: sel services: app.knp.sidebar_menu: class: Knp\Menu\MenuItem factory: ['@App\Menu\MenuBuilder', createSidebarMenu] arguments: ["@request_stack"] tags: - { name: knp_menu.menu, alias: sidebar_menu }
Естественно, в knp_menu.yaml кастомный темлейт (knp_menu.twig.template) и css-класс для активного меню (currentClass) - это все опционально. Если поведение вашего меню совпадает с настройками kmpMenu по умолчанию, то кастомный темлейт нет смысла определять.
alias - указывает имя меня, которое можно будет передать при вызове knp_menu_render.
Благодаря autowiring, в конструкторе MenuBuilder мы можем прописать все зависимости, не создавая явно сервисы под каждую из зависимостей. В данном случае я внедрил 2 зависимости в MenuBuilder: MenuFactory и CategoryRepository, не создавая никаких дополнительных сервисов для них в yaml-файлах.
Содержимое билдера src/Menu/MenuBuilder.php:
<?php namespace App\Menu; use App\Entity\Category; use App\Repository\CategoryRepository; use Knp\Menu\FactoryInterface; use Knp\Menu\ItemInterface; use Symfony\Component\HttpFoundation\RequestStack; class MenuBuilder { /** * @var FactoryInterface */ private $factory; /** * @var CategoryRepository */ private $categoryRepository; /** * @param FactoryInterface $factory * @param CategoryRepository $categoryRepository */ public function __construct( FactoryInterface $factory, CategoryRepository $categoryRepository ) { $this->factory = $factory; $this->categoryRepository = $categoryRepository; } public function createSidebarMenu(RequestStack $requestStack): ItemInterface { $activeParentCategories = $this->categoryRepository ->findActiveParentCategories(); $menu = $this->factory ->createItem('root') ->setChildrenAttribute('class', 'vertical_nav categories_nav'); foreach ($activeParentCategories as $category) { $categoryMenuItem = $this->createMenuItemByCategory($category); $this->addCategoryChildrenMenu($category, $categoryMenuItem); $menu->addChild($categoryMenuItem); } return $menu; } /** * Create menu item using route whether for parent or child category as route is the same for both * * @param Category $category * @return ItemInterface */ private function createMenuItemByCategory(Category $category): ItemInterface { return $this->factory->createItem($category->getName(), [ 'route' => 'category_show', 'routeParameters' => ['category' => $category->getUri()], ]); } private function addCategoryChildrenMenu(Category $category, ItemInterface $categoryMenuItem): void { foreach ($category->getChildren() as $childCategory) { if (!$childCategory->isVisibleInMenu()) { continue; } $childCategoryMenuItem = $this->createMenuItemByCategory($childCategory); $categoryMenuItem->addChild($childCategoryMenuItem); } } }
В моем случае у пунктов меню верхнего уровня могут быть только дети первого уровня. У потомков (детей) не может быть потомков.
Для рендеринга меню в twig-шаблоне достаточно указать:
{{ knp_menu_render('sidebar_menu') }}
где sidebar_menu - алиас, указанный в сервисе app.knp.sidebar_menu в knp_menu.yaml.
В результате будет сгенерировано меню следующего вида:
-
Category 1
- SubCategory 1.1
- SubCategory 1.2
- SubCategory 1.3 - Category 2
-
Category 3
- SubCategory 3.1
- SubCategory 3.2
- SubCategory 3.3
Комментарии 0