Using events to allow a menu to be extended
If you want to let different parts of your system hook into the building of your menu, a good way is to use an approach based on the Symfony EventDispatcher component.
Create the menu builder
Your menu builder will create the base menu item and then dispatch an event to allow other parts of your application to add more stuff to it.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
// src/Menu/MainBuilder.php
namespace App\Menu;
use App\Event\ConfigureMenuEvent;
use Knp\Menu\FactoryInterface;
use Symfony\Component\DependencyInjection\ContainerAwareInterface;
use Symfony\Component\DependencyInjection\ContainerAwareTrait;
class MainBuilder implements ContainerAwareInterface
{
use ContainerAwareTrait;
public function build(FactoryInterface $factory)
{
$menu = $factory->createItem('root');
$menu->addChild('Dashboard', ['route' => '_acp_dashboard']);
$this->container->get('event_dispatcher')->dispatch(
new ConfigureMenuEvent($factory, $menu),
ConfigureMenuEvent::CONFIGURE
);
return $menu;
}
}
Note
This implementation assumes you use the BuilderAliasProvider
(getting
your menu as App:MainBuilder:build
) but you could also define
it as a service and inject the event_dispatcher
service as a dependency.
Create the Event object
The event object allows passing some data to the listener. In this case, it will hold the menu being created and the factory.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37
// src/Event/ConfigureMenuEvent.php
namespace App\Event;
use Knp\Menu\FactoryInterface;
use Knp\Menu\ItemInterface;
use Symfony\Component\EventDispatcher\Event;
class ConfigureMenuEvent extends Event
{
const CONFIGURE = 'app.menu_configure';
private $factory;
private $menu;
public function __construct(FactoryInterface $factory, ItemInterface $menu)
{
$this->factory = $factory;
$this->menu = $menu;
}
/**
* @return \Knp\Menu\FactoryInterface
*/
public function getFactory()
{
return $this->factory;
}
/**
* @return \Knp\Menu\ItemInterface
*/
public function getMenu()
{
return $this->menu;
}
}
That's it. Your builder now provides a hook. Let's see how you can use it!
Create a listener
You can register as many listeners as you want for the event. Let's add one.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
// src/Acme/AdminBundle/EventListener/ConfigureMenuListener.php
namespace Acme\AdminBundle\EventListener;
use App\Event\ConfigureMenuEvent;
class ConfigureMenuListener
{
public function __invoke(ConfigureMenuEvent $event)
{
$menu = $event->getMenu();
$menu->addChild('Matches', ['route' => 'versus_rankedmatch_acp_matches_index']);
$menu->addChild('Participants', ['route' => 'versus_rankedmatch_acp_participants_index']);
}
}
You can now register the listener.
1 2 3 4 5
# config/services.yaml
services:
app.admin_configure_menu_listener:
class: Acme\AdminBundle\EventListener\ConfigureMenuListener
tags: [kernel.event_listener]
You could also create your listener as a subscriber and use the kernel.event_subscriber
tag, which does not have any additional attributes.