问题
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:
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', ], ], ]);
->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', ] );
->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