Closed eved42 closed 8 years ago
menus are not defined as services. Use the knp_menu.menu_provider
service to get your menu (it will use the builder internally):
// get main menu
$menu = $this->get('knp_menu.menu_provider')->get('main');
Ok thanks, it works ! I try to get a particular children in order to set the current state explicitly. But it doesn't work and I have no error.
In my controller :
// get admin menu
$menu = $this->get('knp_menu.menu_provider')->get('admin');
$item = $menu->getChildren('user');
$item['user']->setCurrent(true);
$item['user']->isCurrent() returns true but I don't have the "current" css class on the corresponding li.
Any idea ?
Well, you are passing your $menu
variable to your template, or are you using the menu provider to build the menu again in the template ? If you rebuild it when using it in the template, you will deal with a different Menu object tree, where you manual change won't be available.
A better solution to achieve your use case is probably to handle this logic in your menu builder based on options, and using the knp_menu_get
argument allowing to pass options to the menu provider.
I don't understand very well. Let me explain to you my project.
I want a simple menu without children, like this :
When I go to the corresponding route, users
item is set to current, at this point, everything is ok.
But, if I have a url like this : /users/add
(using route "user_show"), I would like that the current state stay on users
. Actually it desappears.
That's why, in my showAction()
function in my UserController.php
, I want to get my admin menu in order to put the current state manually.
So, I created MenuBuilder.php
with 2 methods (createMainMenu() and createAdminMenu() like in the tutorial) and I use it as a service.
<?php
namespace AppBundle\Menu;
use Knp\Menu\FactoryInterface;
class MenuBuilder
{
private $factory;
/**
* @param FactoryInterface $factory
*
* Add any other dependency you need
*/
public function __construct(FactoryInterface $factory)
{
$this->factory = $factory;
}
public function createAdminMenu(array $options)
{
$menu = $this->factory->createItem('root');
$menu->setChildrenAttribute('class', 'nav nav-sidebar');
// menu architecture
$items = array(
'user' => array(
'title' => 'Users management',
'params' => array(
'label' => 'Users',
'route' => 'user_show'
)
)
);
// menu building
foreach ($items as $name => $item) {
$menu->addChild($name, $item['params']);
# ...
}
return $menu;
}
}
services:
app.menu_builder:
class: AppBundle\Menu\MenuBuilder
arguments: ["@knp_menu.factory"]
tags:
- { name: knp_menu.menu_builder, method: createMainMenu, alias: main }
- { name: knp_menu.menu_builder, method: createAdminMenu, alias: admin }
Then, I render it in my layout.html.twig
{{ knp_menu_render('admin') }}
UserController.php
class UserController extends Controller
{
/**
* @Route("/users", name="user")
*/
public function indexAction() {
return $this->render('users/index.html.twig');
}
/**
* @Route("/users/{action}", name="user_show")
*/
public function showAction($action) {
// get admin menu
$menu = $this->get('knp_menu.menu_provider')->get('admin');
$item = $menu->getChildren('user');
$item['user']->setCurrent(true);
# ...
}
}
What should I change ? Can you explain me in details please. I'm new to Symfony so it's a little bit difficult for me to understand very well, sorry.
Well, your controller builds a menu, then alter it, and then drop the object into nowhere. and then, the template builds a menu again because you ask for a admin
menu.
Instead, you should use one of these alternatives:
$menu
variable from the controller to the template, and then pass this object as argument to knp_menu_render
instead of using a menu alias (so that it renders your modified menu object)move the logic into the menu builder (based on options) and pass the builder options in the template (my snippet assumes that the menu builder wants a current_item
option containing an item name):
{{ knp_menu_render(knp_menu_get('admin', options={current_item: 'user'})) }}
(not recommended at all) move the logic to the template:
{% set menu = knp_menu_get('admin') %}
{% do menu.getChildren('user').user.setCurrent(true) %}
{{ knp_menu_render(menu) }}
rely on the menu matcher system to auto-detect the current item. The bundle has a builtin voter which relies on the current route. you should need to register all routes:
$items = array(
'user' => array(
'title' => 'Users management',
'params' => array(
'label' => 'Users',
'route' => 'user_show', // The route used to generate the URL is added automatically
'extras' => array(
'routes' => array('user_add', 'user_edit'), // Add more route name
),
)
)
);
the best solution in your case is probably the last one, i.e. registering additional matched routes for the menu item.
Ah okay. I didn't understand that this code created, in fact, a new menu !
$menu = $this->get('knp_menu.menu_provider')->get('admin');
I thought that I got back my admin menu that I created in my menu builder... I will try your last solution. I've noticed that I had a route issue, I edited my previous post. Now I have 2 differents routes, one for index and the other one for add action.
Thanks !
Great ! Your last solution is very simple et it's exactly what I was trying to do.
Thank you very much !
Hello everyone,
sorry for this stupid question but I don't find any doc about this.
I followed this symfony tutorial : Creating Menu Builders as Services Now, how can I get my KnpMenu in a controller ?
For example, I have two methods in my builder : createMainMenu() and createAdminMenu().
In my controller, I would like something like this :
But it doesn't work... I don't know the correct syntax. Can you help me ?