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?
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.
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.
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',
]
]);
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.
<?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>
Note that these haven't been fully tested. Leave a comment if I should improve things.
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',
]);
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With