Knp Menu Builder - Route Voter / Dropdown - Сохранение родительского элемента активным при навигации по дочерним страницам

Я новичок в Symfony, и у меня возникла проблема с меню навигации на веб-сайте, где я сейчас работаю.

Мы используем Symfony 3.3 и Knp Menu Builder был выбран для создания меню. Я пытался адаптировать многие ответы, которые я нашел в интернете, но мне это не удалось. И на самом деле, я не могу изменить шаблон ветки или маршруты, поэтому некоторые ответы, которые я нашел в Интернете, также не помогли.

Моя проблема:

У меня есть это меню, где пункт Контракт имеет выпадающее подменю. При наведении курсора на кнопку "Контракт" (родительский элемент) она не выделяется, поскольку не является ссылкой. Я должен щелкнуть по нему, чтобы увидеть выпадающий список с подменю. (обычное раскрывающееся меню) Когда я щелкаю элемент подменю, например, " Добавить контракт", открывается дочерняя страница. Но родительский пункт (Контракт) в главном меню не активен. Тогда пользователь сайта может чувствовать себя потерянным. Поэтому я бы хотел, чтобы родительский элемент раскрывающегося списка был активным при навигации по дочерним страницам.

Хорошо... Я попробовал 2 вещи.

Во-первых, в Редакторе меню я добавил в дополнительные функции различные дочерние маршруты. Как это:

'extras' => [
    'safe_label' => true,
    'routes'      =>
    [
         'contract_create'  => 'front_contract_create',
         'contract_edit'    => 'front_contract_edit',
         'contract_consult' => 'front_contract_consult',
         'add_client'       => 'front_add_client',
         'list_clients'     => 'front_list_client',
     ],
],

Это обычно работает, когда мы хотим активировать родительскую ссылку, когда мы перемещаемся по дочерним страницам. Но это не работает, когда мы используем выпадающее меню, случай здесь.

Так что, если только для тестирования я добавляю "uri" следующим образом:

 $menu = $this->factory->createItem(
        'contracts.title',
        [
            **'uri' => "#",**
            'attributes'         => [
                'class' => 'dropdown',
            ],
        ]

затем, когда я получаю прямой доступ к URL-адресу маршрута "front_contract_create" (например, http://www.blablabla.com/contract-create), родительский элемент является ACTIVE, и он работает. Поэтому, когда я нахожусь на странице, чтобы создать контракт, родительский пункт меню будет активным. НО, если я нажимаю на родительский элемент, чтобы открыть раскрывающийся список, он больше не открывается. (потому что родительский элемент раскрывающегося списка не должен быть ссылкой, я читаю) Поэтому я не могу получить доступ к подменю.

Ну, и второе, что я попробовал, это опция Custom Voter (документация Symfony), учитывая, что маршруты элементов подменю не имеют одинаковый префикс.

Поэтому я попытался создать свой собственный избиратель, но я действительно не знаю, как это сделать. Поэтому я попробовал то, что вы видите ниже (RouteVoter.php), но это не работает. Когда я сбрасываю переменные, все они показывают неадекватные маршруты.

Я пробую лучшие решения или я полностью ошибаюсь во всем, что пытаюсь сделать? Должен ли я действительно использовать эту опцию Voter, или я мог бы изменить способ работы выпадающего?

Заранее спасибо, если кто-то может мне помочь. Вы можете проверить код ниже:


navigation.html.twig

<div class="nav-parent">
    <nav class="navbar">
        {{ knp_menu_render('main', {'allow_safe_labels': true, 'currentClass': 'active'}) }}
    </nav>
</div>
<div class="overlay"></div>

RouteVoter.php

<?php

namespace FrontBundle\Menu;

use Knp\Menu\ItemInterface;
use Knp\Menu\Matcher\Voter\VoterInterface;
use Symfony\Component\HttpFoundation\RequestStack;

class RouteVoter implements VoterInterface
{
    /**
     * @var RequestStack
     */
    private $requestStack;

    /**
     * RouteVoter constructor.
     * @param RequestStack $requestStack
     */
    public function __construct(RequestStack $requestStack)
    {
        $this->requestStack = $requestStack;
    }

    /**
     * @param \Knp\Menu\ItemInterface $item
     * @return bool|null
     */
    public function matchItem(ItemInterface $item)
    {
        $itemUri = $item->getUri();
        $itemExtra = $item->getExtra('routes');
        $currentRequest = $this->requestStack->getCurrentRequest()->getRequestUri();

       if ($itemUri === $this->isCurrent($item)) {
           return true;
       } elseif ($itemExtra !== null && in_array($currentRequest, $itemExtra)) {
           return true;
       }

       return null;
    }


    private function isCurrent(ItemInterface $item)
    {
        $item = $this->requestStack->getCurrentRequest();
        return $item;
    }
}

MenuBuilder.php

<?php

namespace FrontBundle\Menu;

use Knp\Menu\FactoryInterface;
use Knp\Menu\ItemInterface;

/**
 * Class MenuBuilder
 * @package FrontBundle\Menu
 */
class MenuBuilder
{
    /**
     * @var FactoryInterface
     */
    private $factory;

    /**
     * @param FactoryInterface $factory
     */
    public function __construct(FactoryInterface $factory) 
    {
        $this->factory = $factory;
    }

    /**
     * @return mixed
     */
    public function createMainMenu()
    {
        $menu = $this->factory->createItem(
            'root',
            [
                'childrenAttributes' => [
                    'class' => 'nav nav-pills nav-stacked',
                ],
            ]
        );

        $menu->addChild(
            "dashboard",
            [
                'route'  => 'dashboard',
                'extras' => [
                    'safe_label' => true,
                ],
            ]
        );

        $menu->addChild(
            'profile',
            [
                'route'  => "profile",
                'extras' => [
                    'safe_label'  => true,
                    'routes'      =>
                        [
                            'settings'  => 'settings',
                            'account' => 'account',
                        ],
                ],
            ]
        );

        $menu->addChild($this->contractMenu());
        $menu->addChild(
            "social.link",
            [
                'route'  => "social",
                'extras' => [
                    'safe_label' => true,
                ],
            ]
        );

      return $menu;
    }

    /**
     * @return ItemInterface
     */
    private function contractMenu()
    {
        $menu = $this->factory->createItem(
            'contracts.title',
            [
                'attributes'         => [
                    'class' => 'dropdown',
                ],
                'childrenAttributes' => [
                    'class' => 'dropdown-menu submenu2',
                ],
                'labelAttributes'    => [
                    'class'         => 'dropdown-toggle',
                    'data-toggle'   => 'dropdown',
                    'role'          => 'button',
                ],
                'extras'             => [
                    'safe_label' => true,
                    'routes'      =>
                        [
                            'contract_create'  => 'front_contract_create',
                            'contract_edit'    => 'front_contract_edit',
                            'contract_consult' => 'front_contract_consult',
                            'add_client'       => 'front_add_client',
                            'list_clients'     => 'front_list_client',
                        ],
                ],
            ]
        );

        $contract = $menu->addChild(
            "operation.title",
            [
                'attributes' => [
                    'class' => 'dropdown-header',
                ],
            ]
        );

        $contract->addChild(
            "contract.create.link",
            [
                'route' => 'front_contract_create',
            ]
        );

        $contract->addChild(
            "contract.edit.link",
            [
                'route' => 'front_contract_edit',
            ]
        );

        $contract->addChild(
            "contract.consult",
            [
                'route' => 'front_contract_consult',
            ]
        );

        $client = $menu->addChild(
            "client.title",
            [
                'attributes' => [
                    'class' => 'dropdown-header',
                ],
            ]
        );

        $client->addChild(
            "client.add.link",
            [
                'route'  => "front_add_client",
                'extras' => [
                    'safe_label' => true,
                ],
            ]
        );

        $client->addChild(
            "client.list.link",
            [
                'route'  => "front_list_client",
                'extras' => [
                    'safe_label' => true,
                ],
            ]
        );

    return $menu;
}

0 ответов

Другие вопросы по тегам