Symfony2 KnpMenuBundle:设置活动菜单项,即使它不在菜单上


Symfony2 KnpMenuBundle: set active a menu item even when its not on that menu

我创建了我的菜单构建器,它可以工作。我的路线之一是

/database

但这有一个子路由:

database/view/{id}

我不想将视图路由放入菜单项中,因为没有 ID,它将无法工作。

但是我希望当用户在视图上时数据库路由处于活动状态。

我该怎么做?

设法用这个小技巧解决了它:

在菜单中添加所有子项后,但在返回菜单之前,我添加了

$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;
        }

这将检查路由并将所需的菜单设置为活动状态。

它没有记录,但正确的方法是现在(例如):

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

如果不需要参数,可以跳过它们。

$menu->addChild('Item name', ['route' => 'item_route']) 时,值['extras']['routes'][0] = 'item_route'将添加到当前子项。

因此,您需要将路由添加到 ['extra']['routes'] = [] 数组中。

很简单。有三种方法可以做到这一点:

  1. 我推荐这种方式。"额外"数组将被合并(无需在'routes'中复制当前'route'):

    $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' 数组中,整个值将被 key 替换(当由于某种原因您不需要获取 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',
        ]
    ]);
    
我已经在这个

问题上绞尽脑汁了几天,我想我想出了最干净的解决方案来解决这个问题,方法是使用我的路由配置和自定义选民。由于这是关于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默认值添加到路由来配置当前菜单项。可以使用 | 分隔密钥。

/**
 * @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',
]);