Symfony2 KnpMenuBundle: сделать активным пункт меню, даже если его нет в этом меню

Я создал свой конструктор меню, и он работает. Один из моих маршрутов

/database

Но у этого есть дочерний маршрут:

database/view/{id}

Я не хочу указывать маршрут просмотра в пунктах меню, потому что без идентификатора он не будет работать.

Но я хочу, чтобы маршрут базы данных был активен, когда пользователь находится в представлении.

Как я могу это сделать?


person NaGeL182    schedule 03.02.2013    source источник


Ответы (4)


Удалось решить это с помощью этого небольшого взлома:

в меню Буйдер после добавления всех Детей но перед возвратом в меню я добавил

$request = $this->container->get('request');
        $routeName = $request->get('_route');
        switch ($routeName)
        {
            case 'battlemamono_database_view_by_name':
            case 'battlemamono_database_view_by_id':
                $database->setCurrent(true);
                break;
        }

это проверяет маршруты и активирует необходимое меню.

person NaGeL182    schedule 06.02.2013
comment
Спасибо, что опубликовали это! - person Splendonia; 31.01.2014

Это не задокументировано, но правильный способ сделать это сейчас (например):

        $menu->addChild('Category', [
            'route' => 'category_show',
            'routeParameters' => ['slug' => $category->getSlug()],
            'extras' => [
                'routes' => [
                    [
                        'route' => 'thread_show',
                        'parameters' => ['categorySlug' => $category->getSlug()]
                    ],
                ],
            ],
        ]);

Вы можете пропустить параметры, если они вам не нужны.

person Nicholas Ruunu    schedule 04.08.2016
comment
Спасибо за публикацию этого, работает как шарм, но я нахожу его очень подробным, если вам нужно определить несколько маршрутов, например. удалять, добавлять, редактировать, информационные URL-адреса по одной ссылке, возможно ли определить их в файле yml? - person m453h; 19.09.2016
comment
@ m453h, Не то, чтобы я знал об этом, но я не думаю, что вам следует так сильно беспокоиться о многословии. Все не принадлежит файлам yml. :) - person Nicholas Ruunu; 28.09.2016
comment
Что такое переменная $category в классе MenuBuilder? - person Pmpr; 06.04.2017
comment
@Trix, то, что вы хотите, это просто пример, в моем случае у меня была сущность категории со слагом. - person Nicholas Ruunu; 19.04.2017

Когда $menu->addChild('Item name', ['route' => 'item_route']), значение ['extras']['routes'][0] = 'item_route' добавляется к текущему дочернему элементу.

Поэтому вам нужно добавить маршруты в массив ['extra']['routes'] = [] дочернему элементу.

Это просто. Есть три способа сделать это:

  1. Я рекомендую этот способ. Массив extras будет объединен (не нужно дублировать текущий 'route' в 'routes'):

    $menu->addChild('Item name', [
        'route' => 'item_route', // The item link in the menu
        'extras' => [
            // Set current (active) if route matches one of the set (including route from key 'route')
            'routes' => [ 
                'item_route_inner_1',
                'item_route_inner_2',
                'item_route_outside_1',
            ],
        ],
    ]);
    
  2. ->setExtra(key, value) В массиве 'extras' все значение будет заменено на ключ (это может быть полезно, когда по каким-то причинам вам не нужно добираться до маршрутов по пути, указанному в route):

    $menu->addChild('Point name', [
        'route' => 'point_route', // route point (on click)
    ])
    // In the 'extras' array, the entire value will be replaced by the 'routes' key
    ->setExtra(
        'routes', [ // Set current (active) if route matches one of the set
            'point_route', // Can be removed, if necessary
            'point_route_inner_1',
            'point_route_inner_2',
            'point_route_outside_1',
        ]
    );
    
  3. ->setExtras(['routes'] => value) Массив 'extras' будет полностью заменен. Используйте крайне осторожно!

    $menu->addChild('Point name', [
        'route' => 'point_route', // route point (on click)
    ])
    // The 'extras' array will be completely replaced
    ->setExtras([
        'routes' => [
            'point_route', // Can be removed, if necessary
            'point_route_inner_1',
            'point_route_inner_2',
            'point_route_outside_1',
        ]
    ]);
    
person Maxim Mandrik    schedule 08.10.2018

Я ломал голову над этой проблемой в течение нескольких дней, и я думаю, что нашел самое чистое решение для решения этой проблемы, используя мою конфигурацию маршрутизации и собственный Voter. Поскольку речь идет о компоненте маршрутизации Symfony, я предполагаю, что вы используете фреймворк Symfony или имеете достаточно знаний о Symfony, чтобы заставить его работать на себя.

Создайте пользовательского избирателя

<?php
# src/AppBundle/Menu/RouteKeyVoter.php

namespace AppBundle\Menu;

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

/**
 * Voter based on routing configuration
 */
class RouteKeyVoter implements VoterInterface
{
    /**
     * @var Request
     */
    private $request;

    public function __construct(Request $request = null)
    {
        $this->request = $request;
    }

    public function setRequest(Request $request)
    {
        $this->request = $request;
    }

    public function matchItem(ItemInterface $item)
    {
        if (null === $this->request) {
            return null;
        }

        $routeKeys = explode('|', $this->request->attributes->get('_menu_key'));

        foreach ($routeKeys as $key) {
            if (!is_string($key) || $key == '') {
                continue;
            }

            // Compare the key(s) defined in the routing configuration to the name of the menu item
            if ($key == $item->getName()) {
                return true;
            }
        }

        return null;
    }
}

Зарегистрировать избирателя в контейнере

# app/config/services.yml

services:
    # your other services...

    app.menu.route_key_voter:
        class: AppBundle\Menu\RouteKeyVoter
        scope: request
        tags:
            - { name: knp_menu.voter }

<!-- xml -->

<service id="app.menu.route_key_voter" class="AppBundle\Menu\RouteKeyVoter">
    <tag name="knp_menu.voter" request="true"/>
</service>

Обратите внимание, что они не были полностью протестированы. Оставьте комментарий, если я должен улучшить ситуацию.

Настройка клавиш меню

После того, как вы создали собственный Voter и добавили его в настроенный Matcher, вы можете настроить текущие пункты меню, добавив _menu_key default к маршруту. Ключи можно разделить с помощью |.

/**
 * @Route("/articles/{id}/comments",
 *     defaults={
 *         "_menu_key": "article|other_menu_item"
 *     }
 * )
 */
public function commentsAction($id)
{
}

article_comments:
    path:      /articles/{id}/comments
    defaults:
        _controller: AppBundle:Default:comments
        _menu_key:   article|other_menu_item

<route id="article_comments" path="/articles/{id}/comments">
    <default key="_controller">AppBundle:Default:comments</default>
    <default key="_menu_key">article|other_menu_item</default>
</route>

Вышеупомянутые конфигурации будут соответствовать следующему пункту меню:

$menu->addChild('article', [ // <-- matched name of the menu item
    'label' => 'Articles',
    'route' => 'article_index',
]);
person Coded Monkey    schedule 05.05.2017