diff --git a/app/Menu/AdminMenuBuilder.php b/app/Menu/AdminMenuListener.php similarity index 79% rename from app/Menu/AdminMenuBuilder.php rename to app/Menu/AdminMenuListener.php index c16260c67..dd790d25e 100644 --- a/app/Menu/AdminMenuBuilder.php +++ b/app/Menu/AdminMenuListener.php @@ -14,19 +14,16 @@ namespace App\Menu; use Knp\Menu\ItemInterface; -use Sylius\AdminUi\Knp\Menu\MenuBuilderInterface; -use Symfony\Component\DependencyInjection\Attribute\AsDecorator; +use Sylius\AdminUi\Knp\Menu\Event\MenuBuilderEvent; +use Sylius\AdminUi\Knp\Menu\MenuBuilder; +use Symfony\Component\EventDispatcher\Attribute\AsEventListener; -#[AsDecorator(decorates: 'sylius_admin_ui.knp.menu_builder')] -final class AdminMenuBuilder implements MenuBuilderInterface +#[AsEventListener(MenuBuilder::EVENT_NAME)] +final class AdminMenuListener { - public function __construct(private MenuBuilderInterface $menuBuilder) + public function __invoke(MenuBuilderEvent $event): void { - } - - public function createMenu(array $options): ItemInterface - { - $menu = $this->menuBuilder->createMenu($options); + $menu = $event->getMenu(); $menu ->addChild('dashboard', [ @@ -38,8 +35,6 @@ public function createMenu(array $options): ItemInterface $this->addLibrarySubMenu($menu); $this->addConfigurationSubMenu($menu); - - return $menu; } private function addLibrarySubMenu(ItemInterface $menu): void diff --git a/docs/cookbook/admin_panel/menu.md b/docs/cookbook/admin_panel/menu.md index 12c32d210..f621fae38 100644 --- a/docs/cookbook/admin_panel/menu.md +++ b/docs/cookbook/admin_panel/menu.md @@ -10,29 +10,25 @@ -To customize the admin menu, you need to decorate the `sylius_admin_ui.knp.menu_builder` service. +To customize the admin menu, you need to listen for the `sylius_admin_ui.menu.event.main` event. This way, you can implement +multiple listeners e.g. in different bounded contexts of your application. ```php declare(strict_types=1); namespace App\Menu; -use Knp\Menu\FactoryInterface; use Knp\Menu\ItemInterface; -use Sylius\AdminUi\Knp\Menu\MenuBuilderInterface; -use Symfony\Component\DependencyInjection\Attribute\AsDecorator; +use Sylius\AdminUi\Knp\Menu\Event\MenuBuilderEvent; +use Sylius\AdminUi\Knp\Menu\MenuBuilder; +use Symfony\Component\EventDispatcher\Attribute\AsEventListener; -#[AsDecorator(decorates: 'sylius_admin_ui.knp.menu_builder')] -final readonly class MenuBuilder implements MenuBuilderInterface +#[AsEventListener(MenuBuilder::EVENT_NAME)] +final readonly class MenuListener { - public function __construct( - private readonly FactoryInterface $factory, - ) { - } - - public function createMenu(array $options): ItemInterface + public function __invoke(MenuBuilderEvent $event): void { - $menu = $this->factory->createItem('root'); + $menu = $event->getMenu(); $menu ->addChild('dashboard', [ @@ -41,8 +37,6 @@ final readonly class MenuBuilder implements MenuBuilderInterface ->setLabel('sylius.ui.dashboard') ->setLabelAttribute('icon', 'tabler:dashboard') ; - - return $menu; } } ``` @@ -59,18 +53,16 @@ Now you can add submenu items: ```php // ... -#[AsDecorator(decorates: 'sylius_admin_ui.knp.menu_builder')] -final readonly class MenuBuilder implements MenuBuilderInterface +#[AsEventListener(MenuBuilder::EVENT_NAME)] +final readonly class MenuListener { // ... - public function createMenu(array $options): ItemInterface + public function __invoke(MenuBuilderEvent $event): void { - $menu = $this->factory->createItem('root'); + $menu = $event->getMenu(); // ... $this->addLibrarySubMenu($menu); - - return $menu; } private function addLibrarySubMenu(ItemInterface $menu): void diff --git a/src/AdminUi/config/services.php b/src/AdminUi/config/services.php index e86bdba22..f32ba61bc 100644 --- a/src/AdminUi/config/services.php +++ b/src/AdminUi/config/services.php @@ -23,7 +23,7 @@ $services = $configurator->services(); $services->set('sylius_admin_ui.knp.menu_builder', MenuBuilder::class) - ->args([service('knp_menu.factory')]) + ->args([service('knp_menu.factory'), service('event_dispatcher')]) ->tag(name: 'knp_menu.menu_builder', attributes: ['method' => 'createMenu', 'alias' => 'sylius_admin_ui.menu.sidebar']) ; $services->alias(MenuBuilderInterface::class, 'sylius_admin_ui.knp.menu_builder'); diff --git a/src/AdminUi/src/Knp/Menu/Event/MenuBuilderEvent.php b/src/AdminUi/src/Knp/Menu/Event/MenuBuilderEvent.php new file mode 100644 index 000000000..b5b2557da --- /dev/null +++ b/src/AdminUi/src/Knp/Menu/Event/MenuBuilderEvent.php @@ -0,0 +1,37 @@ +factory; + } + + public function getMenu(): ItemInterface + { + return $this->menu; + } +} diff --git a/src/AdminUi/src/Knp/Menu/MenuBuilder.php b/src/AdminUi/src/Knp/Menu/MenuBuilder.php index 5e1387713..a914cc1e4 100644 --- a/src/AdminUi/src/Knp/Menu/MenuBuilder.php +++ b/src/AdminUi/src/Knp/Menu/MenuBuilder.php @@ -15,15 +15,28 @@ use Knp\Menu\FactoryInterface; use Knp\Menu\ItemInterface; +use Sylius\AdminUi\Knp\Menu\Event\MenuBuilderEvent; +use Symfony\Contracts\EventDispatcher\EventDispatcherInterface; final class MenuBuilder implements MenuBuilderInterface { - public function __construct(private readonly FactoryInterface $factory) - { + public const EVENT_NAME = 'sylius_admin_ui.menu.event.main'; + + public function __construct( + private readonly FactoryInterface $factory, + private readonly EventDispatcherInterface $eventDispatcher, + ) { } public function createMenu(array $options): ItemInterface { - return $this->factory->createItem('root'); + $root = $this->factory->createItem('root'); + + $this->eventDispatcher->dispatch( + new MenuBuilderEvent($this->factory, $root), + self::EVENT_NAME, + ); + + return $root; } } diff --git a/src/AdminUi/tests/Unit/Knp/Menu/MenuBuilderTest.php b/src/AdminUi/tests/Unit/Knp/Menu/MenuBuilderTest.php new file mode 100644 index 000000000..97eed28fe --- /dev/null +++ b/src/AdminUi/tests/Unit/Knp/Menu/MenuBuilderTest.php @@ -0,0 +1,45 @@ +createMock(FactoryInterface::class); + $eventDispatcher = $this->createMock(EventDispatcherInterface::class); + $menuBuilder = new MenuBuilder($factory, $eventDispatcher); + + $root = new MenuItem('root', $factory); + + $factory + ->expects($this->once()) + ->method('createItem') + ->with('root') + ->willReturn($root) + ; + + $event = new MenuBuilderEvent($factory, $root); + $eventDispatcher->expects($this->once())->method('dispatch')->with($event, 'sylius_admin_ui.menu.event.main'); + + self::assertSame($root, $menuBuilder->createMenu([])); + } +} diff --git a/tests/Functional/BookTest.php b/tests/Functional/BookTest.php index 576365dc8..223b2ebcf 100644 --- a/tests/Functional/BookTest.php +++ b/tests/Functional/BookTest.php @@ -300,4 +300,17 @@ public function testRemovingBook(): void $this->assertCount(0, BookFactory::all()); } + + public function testShowingMenu(): void + { + $this->client->request('GET', '/admin/books'); + + self::assertSelectorExists('#sidebar-menu'); + + self::assertAnySelectorTextContains('#sidebar-menu [href="#navbar-library"]', 'Library'); + self::assertAnySelectorTextContains('#sidebar-menu [href="#navbar-configuration"]', 'Configuration'); + self::assertAnySelectorTextContains('#sidebar-menu [href="/admin/conferences"]', 'Conferences'); + self::assertAnySelectorTextContains('#sidebar-menu [href="/admin/talks"]', 'Talks'); + self::assertAnySelectorTextContains('#sidebar-menu [href="/admin/speakers"]', 'Speakers'); + } }