diff --git a/src/Menu/Provider/GroupMenuProvider.php b/src/Menu/Provider/GroupMenuProvider.php index 50405ccb5e..fe07cacd30 100644 --- a/src/Menu/Provider/GroupMenuProvider.php +++ b/src/Menu/Provider/GroupMenuProvider.php @@ -17,6 +17,7 @@ use Knp\Menu\ItemInterface; use Knp\Menu\Provider\MenuProviderInterface; use Sonata\AdminBundle\Admin\Pool; +use Symfony\Component\Routing\Generator\UrlGeneratorInterface; use Symfony\Component\Security\Core\Authorization\AuthorizationCheckerInterface; /** @@ -172,7 +173,11 @@ private function generateMenuItem(array $item, array $group): ItemInterface if (isset($item['admin']) && !empty($item['admin'])) { $admin = $this->pool->getInstance($item['admin']); - $options = $admin->generateMenuUrl('list', [], $item['route_absolute']); + $options = $admin->generateMenuUrl( + 'list', + [], + $item['route_absolute'] ? UrlGeneratorInterface::ABSOLUTE_URL : UrlGeneratorInterface::ABSOLUTE_PATH + ); $options['extras'] = [ 'label_catalogue' => $admin->getTranslationDomain(), 'admin' => $admin, diff --git a/src/Route/DefaultRouteGenerator.php b/src/Route/DefaultRouteGenerator.php index c6668ce8a5..edf666d856 100644 --- a/src/Route/DefaultRouteGenerator.php +++ b/src/Route/DefaultRouteGenerator.php @@ -63,7 +63,11 @@ public function generateUrl( ) { $arrayRoute = $this->generateMenuUrl($admin, $name, $parameters, $absolute); - return $this->router->generate($arrayRoute['route'], $arrayRoute['routeParameters'], $arrayRoute['routeAbsolute']); + return $this->router->generate( + $arrayRoute['route'], + $arrayRoute['routeParameters'], + $arrayRoute['routeAbsolute'] ? UrlGeneratorInterface::ABSOLUTE_URL : UrlGeneratorInterface::ABSOLUTE_PATH + ); } public function generateMenuUrl( @@ -116,7 +120,7 @@ public function generateMenuUrl( return [ 'route' => $this->caches[$code], 'routeParameters' => $parameters, - 'routeAbsolute' => $absolute, + 'routeAbsolute' => UrlGeneratorInterface::ABSOLUTE_URL === $absolute, ]; } diff --git a/tests/Menu/Provider/GroupMenuProviderTest.php b/tests/Menu/Provider/GroupMenuProviderTest.php index 76c75c177e..43e242d242 100644 --- a/tests/Menu/Provider/GroupMenuProviderTest.php +++ b/tests/Menu/Provider/GroupMenuProviderTest.php @@ -14,6 +14,7 @@ namespace Sonata\AdminBundle\Tests\Menu\Provider; use Knp\Menu\FactoryInterface; +use Knp\Menu\Integration\Symfony\RoutingExtension; use Knp\Menu\ItemInterface; use Knp\Menu\MenuFactory; use Knp\Menu\MenuItem; @@ -23,6 +24,7 @@ use Sonata\AdminBundle\Admin\AbstractAdmin; use Sonata\AdminBundle\Admin\Pool; use Sonata\AdminBundle\Menu\Provider\GroupMenuProvider; +use Symfony\Component\Routing\Generator\UrlGeneratorInterface; use Symfony\Component\Security\Core\Authorization\AuthorizationCheckerInterface; class GroupMenuProviderTest extends TestCase @@ -56,6 +58,20 @@ protected function setUp(): void $this->factory = new MenuFactory(); + $urlGenerator = $this->createStub(UrlGeneratorInterface::class); + $urlGenerator->method('generate')->willReturnCallback(static function (string $name, array $parameters = [], int $referenceType = UrlGeneratorInterface::ABSOLUTE_PATH): string { + switch ($referenceType) { + case UrlGeneratorInterface::ABSOLUTE_URL: + return 'http://sonata-project/'.$name.($parameters ? '?'.http_build_query($parameters) : ''); + case UrlGeneratorInterface::ABSOLUTE_PATH: + return '/'.$name.($parameters ? '?'.http_build_query($parameters) : ''); + default: + throw new \InvalidArgumentException(sprintf('Dummy router does not support the reference type "%s".', $referenceType)); + } + }); + + $this->factory->addExtension(new RoutingExtension($urlGenerator)); + $this->provider = new GroupMenuProvider($this->factory, $this->pool, $this->checker); } @@ -92,7 +108,7 @@ public function testGroupMenuProviderWithoutChecker(array $adminGroups): void $children = $menu->getChildren(); - $this->assertCount(2, $children); + $this->assertCount(3, $children); $this->assertArrayHasKey('foo_admin_label', $children); $this->assertArrayHasKey('route_label', $children); $this->assertInstanceOf(MenuItem::class, $menu['foo_admin_label']); @@ -255,7 +271,7 @@ public function testGetMenuProviderWithAdmin(array $adminGroups): void $children = $menu->getChildren(); - $this->assertCount(2, $children); + $this->assertCount(3, $children); $this->assertArrayHasKey('foo_admin_label', $children); $this->assertArrayHasKey('route_label', $children); $this->assertInstanceOf(MenuItem::class, $menu['foo_admin_label']); @@ -263,11 +279,14 @@ public function testGetMenuProviderWithAdmin(array $adminGroups): void $extras = $menu['foo_admin_label']->getExtras(); $this->assertArrayHasKey('label_catalogue', $extras); - $this->assertSame($extras['label_catalogue'], 'SonataAdminBundle'); + $this->assertSame('SonataAdminBundle', $extras['label_catalogue']); $extras = $menu['route_label']->getExtras(); $this->assertArrayHasKey('label_catalogue', $extras); - $this->assertSame($extras['label_catalogue'], 'SonataAdminBundle'); + $this->assertSame('SonataAdminBundle', $extras['label_catalogue']); + + $this->assertSame('http://sonata-project/FooRoute?foo=bar', $menu['route_label']->getUri()); + $this->assertSame('/FooRelativeRoute?baz=qux', $menu['relative_route']->getUri()); } /** @@ -295,7 +314,7 @@ public function testGetKnpMenuWithListRoute(array $adminGroups): void $this->assertInstanceOf(ItemInterface::class, $menu); $this->assertArrayNotHasKey('foo_admin_label', $menu->getChildren()); $this->assertArrayHasKey('route_label', $menu->getChildren()); - $this->assertCount(1, $menu->getChildren()); + $this->assertCount(2, $menu->getChildren()); } /** @@ -323,7 +342,7 @@ public function testGetKnpMenuWithGrantedList(array $adminGroups): void $this->assertInstanceOf(ItemInterface::class, $menu); $this->assertArrayNotHasKey('foo_admin_label', $menu->getChildren()); $this->assertArrayHasKey('route_label', $menu->getChildren()); - $this->assertCount(1, $menu->getChildren()); + $this->assertCount(2, $menu->getChildren()); } /** @@ -378,9 +397,33 @@ public function testGetMenuProviderKeepOpenOption(array $adminGroups): void } /** - * @return array + * @dataProvider getRootMenuItemWithDifferentUrlTypes */ - public function getAdminGroups() + public function testRootMenuItemUrl(string $expectedUrl, array $item): void + { + $this->pool + ->method('getInstance') + ->with($this->equalTo('sonata_admin_absolute_url')) + ->willReturn($this->getAdminMock()); + + $this->checker + ->method('isGranted') + ->willReturn(true); + + $menu = $this->provider->get( + 'providerFoo', + [ + 'name' => 'foo', + 'group' => $item, + ] + ); + + $this->assertInstanceOf(ItemInterface::class, $menu); + $this->assertSame('foo', $menu->getName()); + $this->assertSame($expectedUrl, $menu['foo_admin_label']->getUri()); + } + + public function getAdminGroups(): array { return [ [ @@ -392,6 +435,7 @@ public function getAdminGroups() [ 'admin' => 'sonata_admin_foo_service', 'label' => 'fooLabel', + 'route' => 'FooServiceRoute', 'route_absolute' => true, ], [ @@ -402,6 +446,14 @@ public function getAdminGroups() 'route_absolute' => true, 'roles' => [], ], + [ + 'admin' => '', + 'label' => 'relative_route', + 'route' => 'FooRelativeRoute', + 'route_params' => ['baz' => 'qux'], + 'route_absolute' => false, + 'roles' => [], + ], ], 'item_adds' => [], 'roles' => ['foo'], @@ -410,10 +462,7 @@ public function getAdminGroups() ]; } - /** - * @return array - */ - public function getAdminGroupsMultipleRoles() + public function getAdminGroupsMultipleRoles(): array { return [ [ @@ -506,10 +555,7 @@ public function getAdminGroupsMultipleRoles() ]; } - /** - * @return array - */ - public function getAdminGroupsMultipleRolesOnTop() + public function getAdminGroupsMultipleRolesOnTop(): array { return [ [ @@ -570,10 +616,7 @@ public function getAdminGroupsMultipleRolesOnTop() ]; } - /** - * @return array - */ - public function getAdminGroupsWithOnTopOption() + public function getAdminGroupsWithOnTopOption(): array { return [ [ @@ -597,6 +640,47 @@ public function getAdminGroupsWithOnTopOption() ]; } + public function getRootMenuItemWithDifferentUrlTypes(): iterable + { + yield 'absolute_url' => [ + 'http://sonata-project/list', + [ + 'label' => 'foo', + 'icon' => '', + 'label_catalogue' => 'SonataAdminBundle', + 'items' => [ + [ + 'admin' => 'sonata_admin_absolute_url', + 'label' => 'fooLabel', + 'route' => 'FooAbsoulteRoute', + 'route_absolute' => true, + ], + ], + 'item_adds' => [], + 'roles' => ['foo'], + ], + ]; + + yield 'absolute_path' => [ + '/list', + [ + 'label' => 'foo', + 'icon' => '', + 'label_catalogue' => 'SonataAdminBundle', + 'items' => [ + [ + 'admin' => 'sonata_admin_absolute_url', + 'label' => 'fooLabel', + 'route' => 'FooAbsolutePath', + 'route_absolute' => false, + ], + ], + 'item_adds' => [], + 'roles' => ['foo'], + ], + ]; + } + private function getAdminMock(bool $hasRoute = true, bool $isGranted = true): AbstractAdmin { $admin = $this->createMock(AbstractAdmin::class); @@ -616,7 +700,17 @@ private function getAdminMock(bool $hasRoute = true, bool $isGranted = true): Ab $admin ->method('generateMenuUrl') - ->willReturn([]); + ->willReturnCallback(static function (string $name, array $parameters = [], int $referenceType = UrlGeneratorInterface::ABSOLUTE_PATH): array { + if (!\in_array($referenceType, [UrlGeneratorInterface::ABSOLUTE_URL, UrlGeneratorInterface::ABSOLUTE_PATH], true)) { + throw new \InvalidArgumentException(sprintf('Dummy router does not support the reference type "%s".', $referenceType)); + } + + return [ + 'route' => $name, + 'routeParameters' => $parameters, + 'routeAbsolute' => UrlGeneratorInterface::ABSOLUTE_URL === $referenceType, + ]; + }); $admin ->method('getTranslationDomain') diff --git a/tests/Route/DefaultRouteGeneratorTest.php b/tests/Route/DefaultRouteGeneratorTest.php index 7c01c59979..b166f91451 100644 --- a/tests/Route/DefaultRouteGeneratorTest.php +++ b/tests/Route/DefaultRouteGeneratorTest.php @@ -26,6 +26,8 @@ class DefaultRouteGeneratorTest extends TestCase { + private const ROUTER_DOMAIN = 'http://sonata-project'; + protected $cacheTempFolder; public function setUp(): void @@ -51,8 +53,12 @@ public function testGenerate(): void /** * @dataProvider getGenerateUrlTests */ - public function testGenerateUrl(string $expected, string $name, array $parameters): void - { + public function testGenerateUrl( + string $expected, + string $name, + array $parameters, + int $referenceType = RouterInterface::ABSOLUTE_PATH + ): void { $childCollection = new RouteCollection('base.Code.Foo|base.Code.Bar', 'admin_acme_child', '/foo/', 'BundleName:ControllerName'); $childCollection->add('bar'); @@ -74,17 +80,18 @@ public function testGenerateUrl(string $expected, string $name, array $parameter $router = $this->getMockForAbstractClass(RouterInterface::class); $router->expects($this->once()) ->method('generate') - ->willReturnCallback(static function (string $name, array $parameters = []): string { + ->willReturnCallback(static function (string $name, array $parameters = [], int $referenceType = RouterInterface::ABSOLUTE_PATH): string { $params = ''; + $domain = RouterInterface::ABSOLUTE_URL === $referenceType ? self::ROUTER_DOMAIN : ''; if (!empty($parameters)) { $params .= '?'.http_build_query($parameters); } switch ($name) { case 'admin_acme_foo': - return '/foo'.$params; + return $domain.'/foo'.$params; case 'admin_acme_child_bar': - return '/foo/bar'.$params; + return $domain.'/foo/bar'.$params; } }); @@ -92,7 +99,7 @@ public function testGenerateUrl(string $expected, string $name, array $parameter $generator = new DefaultRouteGenerator($router, $cache); - $this->assertSame($expected, $generator->generateUrl($admin, $name, $parameters)); + $this->assertSame($expected, $generator->generateUrl($admin, $name, $parameters, $referenceType)); } public function getGenerateUrlTests(): array @@ -100,6 +107,13 @@ public function getGenerateUrlTests(): array return [ ['/foo?abc=a123&efg=e456&default_param=default_val', 'foo', ['default_param' => 'default_val']], ['/foo/bar?abc=a123&efg=e456&default_param=default_val', 'base.Code.Bar.bar', ['default_param' => 'default_val']], + ['/foo/bar?abc=a123&efg=e456&default_param=default_val', 'base.Code.Bar.bar', ['default_param' => 'default_val'], RouterInterface::ABSOLUTE_PATH], + [ + self::ROUTER_DOMAIN.'/foo/bar?abc=a123&efg=e456&default_param=default_val', + 'base.Code.Bar.bar', + ['default_param' => 'default_val'], + RouterInterface::ABSOLUTE_URL, + ], ]; }