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

余生颓废 提交于 2019-12-10 14:00:04

问题


I created my menu builder and it works. One of my route is

/database

But this has a child route:

database/view/{id}

I don't want to put the view route into the menu items because without the ID it won't work.

But I want the database route to be active when the user is on the view.

How can I do this?


回答1:


Managed to solve it with this little hack:

in the menuBuider after you add all the Children but before you return the menu i added

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

this checks the routes and sets the needed menu active.




回答2:


It's not documented, but the correct way of doing this is now (ex):

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

You can skip the parameters if you don't need them.




回答3:


I've been racking my brain on this problem for a couple of days and I think I came up with the cleanest solution to solve this problem by using my routing configuration and a custom Voter. Since this is about the Symfony Routing component I'm assuming you're using the Symfony framework or have enough knowledge of Symfony to make it work for yourself.

Create a custom Voter

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

Register the voter in the container

# 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>

Note that these haven't been fully tested. Leave a comment if I should improve things.

Configuring the menu keys

Once you've created the custom Voter and added it to the configured Matcher, you can configure the current menu items by adding the _menu_key default to a route. Keys can be seperated by using |.

/**
 * @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>

The above configurations would match the following menu item:

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



回答4:


When $menu->addChild('Item name', ['route' => 'item_route']), the value ['extras']['routes'][0] = 'item_route' is added to the current child.

So you need to add routes to the ['extra']['routes'] = [] array to child.

It's simple. There are three ways to do this:

  1. I recommend this way. The 'extras' array will be merge (no need to duplicate the current 'route' in '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) In the 'extras' array, the entire value will be replaced by the key (it can be useful when for some reason you do not need to get to routes path specified in 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) The 'extras' array will be completely replaced. Use extremely carefully!

    $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',
        ]
    ]);
    


来源:https://stackoverflow.com/questions/14674820/symfony2-knpmenubundle-set-active-a-menu-item-even-when-its-not-on-that-menu

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!