From ab7a0c9763b935252cb1c12d08f67cd2c7ba617b Mon Sep 17 00:00:00 2001 From: David Maicher Date: Thu, 29 Aug 2024 13:53:36 +0200 Subject: [PATCH] add ability to match request for node activation (#46) --- README.md | 4 ++++ src/Node/Node.php | 26 ++++++++++++++++++++++++ src/NodeVisitor/NodeActivator.php | 13 ++++++++---- tests/Functional/FunctionalTest.php | 4 ++++ tests/Functional/TestMenuTreeBuilder.php | 4 ++++ tests/Functional/routing.yml | 6 ++++++ 6 files changed, 53 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 051c679..1847fdb 100644 --- a/README.md +++ b/README.md @@ -18,6 +18,10 @@ class MainMenuTreeBuilder implements MenuTreeBuilderInterface ->child('stream') ->setRoute('social_media_stream') ->setRequiredPermissions(['ROLE_SOCIAL_STREAM']) + ->setAdditionalActiveRequestMatcher(static function (Request $request): bool { + // additionally will mark node as active if the request path starts with '/foo_bar' + return str_starts_with($request->getPathInfo(), '/foo_bar'); + }) ->end() ->child('update_status') ->setRoute('social_media_update_status') diff --git a/src/Node/Node.php b/src/Node/Node.php index 4415ced..b8c06ad 100644 --- a/src/Node/Node.php +++ b/src/Node/Node.php @@ -3,6 +3,7 @@ namespace DAMA\MenuBundle\Node; use Symfony\Component\ExpressionLanguage\Expression; +use Symfony\Component\HttpFoundation\Request; class Node { @@ -46,6 +47,11 @@ class Node */ protected $additionalActiveRoutes = []; + /** + * @var callable(Request): bool|null + */ + protected $additionalActiveRequestMatcher; + /** * @var array */ @@ -456,4 +462,24 @@ public static function resetCounter(): void { self::$counter = 0; } + + /** + * @return callable(Request): bool|null + */ + public function getAdditionalActiveRequestMatcher(): ?callable + { + return $this->additionalActiveRequestMatcher; + } + + /** + * @param callable(Request): bool|null $additionalActiveRequestMatcher + * + * @return $this + */ + public function setAdditionalActiveRequestMatcher(?callable $additionalActiveRequestMatcher): self + { + $this->additionalActiveRequestMatcher = $additionalActiveRequestMatcher; + + return $this; + } } diff --git a/src/NodeVisitor/NodeActivator.php b/src/NodeVisitor/NodeActivator.php index 09515bf..8c459d1 100644 --- a/src/NodeVisitor/NodeActivator.php +++ b/src/NodeVisitor/NodeActivator.php @@ -10,10 +10,7 @@ */ final class NodeActivator implements NodeVisitorInterface { - /** - * @var RequestStack - */ - private $requestStack; + private RequestStack $requestStack; public function __construct(RequestStack $requestStack) { @@ -28,6 +25,14 @@ public function visit(Node $node): void if (in_array($request->get('_route'), $node->getAllActiveRoutes())) { $node->setActive(true); + + return; + } + + if (($callable = $node->getAdditionalActiveRequestMatcher()) + && $callable($request) + ) { + $node->setActive(true); } } } diff --git a/tests/Functional/FunctionalTest.php b/tests/Functional/FunctionalTest.php index 111ebca..8c8dc84 100644 --- a/tests/Functional/FunctionalTest.php +++ b/tests/Functional/FunctionalTest.php @@ -25,6 +25,10 @@ public function testRenderMenuWithUserFoo(): void $this->assertCount(2, $crawler->filter('a[href="/test_2"]')); $this->assertCount(0, $crawler->filter('a[href="/test_1"]')); + + $crawler = $client->request('GET', '/test_3'); + $this->assertSame(200, $client->getResponse()->getStatusCode()); + $this->assertCount(2, $crawler->filter('li.menu-item-active > a[href="/test_2"]')); } public function testRenderMenuWithUserBar(): void diff --git a/tests/Functional/TestMenuTreeBuilder.php b/tests/Functional/TestMenuTreeBuilder.php index f526a25..de0b679 100644 --- a/tests/Functional/TestMenuTreeBuilder.php +++ b/tests/Functional/TestMenuTreeBuilder.php @@ -4,6 +4,7 @@ use DAMA\MenuBundle\MenuTree\MenuTreeBuilderInterface; use DAMA\MenuBundle\Node\Node; +use Symfony\Component\HttpFoundation\Request; class TestMenuTreeBuilder implements MenuTreeBuilderInterface { @@ -29,6 +30,9 @@ public function build(Node $root): void ->end() ->child('bar-sub-2') ->setRoute('test_2') + ->setAdditionalActiveRequestMatcher(static function (Request $request): bool { + return str_starts_with($request->getPathInfo(), '/test_'); + }) ->end() ->end() ; diff --git a/tests/Functional/routing.yml b/tests/Functional/routing.yml index 936780e..6462399 100644 --- a/tests/Functional/routing.yml +++ b/tests/Functional/routing.yml @@ -15,3 +15,9 @@ test_2: controller: 'Symfony\Bundle\FrameworkBundle\Controller\TemplateController::templateAction' defaults: template: test_menu.html.twig + +test_3: + path: /test_3 + controller: 'Symfony\Bundle\FrameworkBundle\Controller\TemplateController::templateAction' + defaults: + template: test_menu.html.twig