knpMenu can't add dynamique route

181 Views Asked by At

I'm working with symfony 3.3 and knpMenu 2.2 I have register a menu builder as service

    lilworks_store.menu_builder:
    class: LilWorks\StoreBundle\Menu\MenuBuilder
    arguments: ["@knp_menu.factory","@request_stack"]
    tags:
        - { name: knp_menu.menu_builder, method: createMainMenu, alias: main } # The alias is what is used to retrieve the menu

I can render a simple menu without any issue. But when I try to had route with parameters I have a error

An exception has been thrown during the rendering of a template ("Parameter "customer_id" for route "customer_show" must match "[^/]++" ("" given) to generate a corresponding URL.").

I can avoid this within a condition on $this->requestStack->getCurrentRequest()->get('customer_id') to create the child only if the route parameter is available.

class MenuBuilder
{


private $factory;
private $requestStack;

/**
 * @param FactoryInterface $factory
 *
 * Add any other dependency you need
 */
public function __construct(FactoryInterface $factory,RequestStack $requestStack)
{
    $this->factory = $factory;
    $this->requestStack = $requestStack;
}
public function createMainMenu(array $options)
{

    $menu = $this->factory->createItem('root');

    $menu->addChild('storebundle.menu.storehome', array('route' => 'site_homepage'));
    $menu->addChild('storebundle.menu.adminhome', array('route' => 'lilworks_store_homepage'));
    $userMenuCat = $menu->addChild('storebundle.menu.cat.user');

    $customerMenu = $userMenuCat->addChild('storebundle.menu.customer', array('route' => 'customer_index'));
    $customerMenu->addChild('storebundle.menu.customer.new', array('route' => 'customer_new'));
    if($this->requestStack->getCurrentRequest()->get('customer_id')){
        $customerMenu->addChild('storebundle.menu.customer.show', array('route' => 'customer_show', 'routeParameters' => array('customer_id' => $this->requestStack->getCurrentRequest()->get('customer_id'))));
        $customerMenu->addChild('storebundle.menu.customer.edit', array('route' => 'customer_edit', 'routeParameters' => array('customer_id' => $this->requestStack->getCurrentRequest()->get('customer_id'))));
    }

    $userMenu =  $userMenuCat->addChild('storebundle.menu.user', array('route' => 'user_index'));
    $userMenu->addChild('storebundle.menu.user.new', array('route' => 'user_new'));
    if($this->requestStack->getCurrentRequest()->get('user_id')){
        #$userMenu->addChild('storebundle.menu.user.show', array('route' => 'user_show', 'routeParameters' => array('user_id' => $this->requestStack->getCurrentRequest()->get('user_id'))));
        #$userMenu->addChild('storebundle.menu.user.edit', array('route' => 'user_edit', 'routeParameters' => array('user_id' => $this->requestStack->getCurrentRequest()->get('user_id'))));
    }

    $sessionMenu = $userMenuCat->addChild('storebundle.menu.session', array('route' => 'session_index'));
    if($this->requestStack->getCurrentRequest()->get('session_id')){
        #$sessionMenu->addChild('storebundle.menu.session.show', array('route' => 'session_show', 'routeParameters' => array('session_id' => $this->requestStack->getCurrentRequest()->get('session_id'))));
        #$sessionMenu->addChild('storebundle.menu.session.edit', array('route' => 'session_edit', 'routeParameters' => array('session_id' => $this->requestStack->getCurrentRequest()->get('session_id'))));
    }


    $orderMenuCat = $menu->addChild('storebundle.menu.cat.order');
    $orderMenu = $orderMenuCat->addChild('storebundle.menu.order', array('route' => 'order_index'));
    $orderMenu->addChild('storebundle.menu.order.new', array('route' => 'order_new'));
    if($this->requestStack->getCurrentRequest()->get('order_id')){
        #$orderMenu->addChild('storebundle.menu.order.show', array('route' => 'order_show', 'routeParameters' => array('order_id' => $this->requestStack->getCurrentRequest()->get('order_id'))));
        #$orderMenu->addChild('storebundle.menu.order.edit', array('route' => 'order_edit', 'routeParameters' => array('order_id' => $this->requestStack->getCurrentRequest()->get('order_id'))));
    }

    $couponMenu = $orderMenuCat->addChild('storebundle.menu.coupon', array('route' => 'coupon_index'));
    $couponMenu->addChild('storebundle.menu.coupon.new', array('route' => 'coupon_new'));
    if($this->requestStack->getCurrentRequest()->get('coupon_id')){
        #$couponMenu->addChild('storebundle.menu.coupon.show', array('route' => 'coupon_show', 'routeParameters' => array('coupon_id' => $this->requestStack->getCurrentRequest()->get('coupon_id'))));
        #$couponMenu->addChild('storebundle.menu.coupon.edit', array('route' => 'coupon_edit', 'routeParameters' => array('coupon_id' => $this->requestStack->getCurrentRequest()->get('coupon_id'))));
    }
    $depositSaleMenu = $orderMenuCat->addChild('storebundle.menu.depositsale', array('route' => 'coupon_index'));
    $depositSaleMenu->addChild('storebundle.menu.depositsale.new', array('route' => 'depositSale_new'));
    if($this->requestStack->getCurrentRequest()->get('depositsale_id')){
        #$depositSaleMenu->addChild('storebundle.menu.coupon.show', array('route' => 'depositSale_show', 'routeParameters' => array('depositsale_id' => $this->requestStack->getCurrentRequest()->get('depositsale_id'))));
        #$depositSaleMenu->addChild('storebundle.menu.coupon.edit', array('route' => 'depositSale_edit', 'routeParameters' => array('depositsale_id' => $this->requestStack->getCurrentRequest()->get('depositsale_id'))));
    }
    return $menu;
}

In all exemple I have seen nobody use this condition to build the menu with parameted route. What I'm doing wrong?

1

There are 1 best solutions below

0
Alister Bulman On BEST ANSWER

You aren't doing anything wrong. If you need information from the request, such as a parameter from the URL, or the username from a service (like $this->tokenStorage->getToken()->getUser();) then arrange to get it, and use it.

In my own code, I've fetched the user (as above - assuming it exists) an use it to make a link:

    $menu->addChild('menu.edit_settings', [
        'route' => 'ca_profile_update',
        'routeParameters' => array('username' => $user->getUsernameCanonical()),
    ]);

For you, the only thing I would particularly do was to get the various parameters ('user_id' and so on) once into a variable and then use it, rather than continually re-fetching it (that's a little too verbose in my opinion). That and just split the menu service up a little into smaller functions to make it a little easier to understand as well - but that is more style and code standards than the original question.

$this->addCustomerMenu(
    $userMenuCat,
    $this->requestStack->getCurrentRequest()->getInt('customer_id', 0)
);

public function addCustomerMenu(MenuItem $userMenuCat, int $customer_id): void
{
    $customerMenu = $userMenuCat->addChild('storebundle.menu.customer', array('route' => 'customer_index'));
    $customerMenu->addChild('storebundle.menu.customer.new', array('route' => 'customer_new'));
    if ($customer_id){
        $customerMenu->addChild('storebundle.menu.customer.show', array('route' => 'customer_show', 'routeParameters' => array('customer_id' => $customer_id)));
        $customerMenu->addChild('storebundle.menu.customer.edit', array('route' => 'customer_edit', 'routeParameters' => array('customer_id' => $customer_id)));
    }
}