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