From 71f7e53b630d408340e984690b6c31e11160c5fb Mon Sep 17 00:00:00 2001 From: Orban Florin Date: Wed, 18 Oct 2023 17:53:10 +0300 Subject: [PATCH] Add callback voter --- CHANGELOG.md | 4 ++ doc/05-Matcher.md | 1 + src/Knp/Menu/Matcher/Voter/CallbackVoter.php | 26 ++++++++ .../Tests/Matcher/Voter/CallbackVoterTest.php | 64 +++++++++++++++++++ 4 files changed, 95 insertions(+) create mode 100644 src/Knp/Menu/Matcher/Voter/CallbackVoter.php create mode 100644 tests/Knp/Menu/Tests/Matcher/Voter/CallbackVoterTest.php diff --git a/CHANGELOG.md b/CHANGELOG.md index 3e80a180..25c211bb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,7 @@ +## 3.5 (unreleased) + +* Added CallbackVoter + ## 3.4 (2023-05-17) * Removed support for unsupported PHP version 7.4 diff --git a/doc/05-Matcher.md b/doc/05-Matcher.md index e7920748..db9329c9 100644 --- a/doc/05-Matcher.md +++ b/doc/05-Matcher.md @@ -33,6 +33,7 @@ KnpMenu provides some voters for standard cases: * `RegexVoter`: checks if the request matches a regular expression you pass to the voter * `RouteVoter`: uses a Symfony request to check if the current route is same as the route of the menu item * `UriVoter`: compare the URI of the menu item with the URI passed to the voter +* `CallbackVoter`: allows matching based on a callback set as `match_callback` under `extras` option of the menu item Create your own voters ---------------------- diff --git a/src/Knp/Menu/Matcher/Voter/CallbackVoter.php b/src/Knp/Menu/Matcher/Voter/CallbackVoter.php new file mode 100644 index 00000000..08e4d02e --- /dev/null +++ b/src/Knp/Menu/Matcher/Voter/CallbackVoter.php @@ -0,0 +1,26 @@ +getExtra('match_callback'); + + if (null === $callback) { + return null; + } + + if (!\is_callable($callback)) { + throw new \InvalidArgumentException('Extra "match_callback" must be callable.'); + } + + return $callback(); + } +} diff --git a/tests/Knp/Menu/Tests/Matcher/Voter/CallbackVoterTest.php b/tests/Knp/Menu/Tests/Matcher/Voter/CallbackVoterTest.php new file mode 100644 index 00000000..8efd4324 --- /dev/null +++ b/tests/Knp/Menu/Tests/Matcher/Voter/CallbackVoterTest.php @@ -0,0 +1,64 @@ +createMock(ItemInterface::class); + $item->expects($this->once()) + ->method('getExtra') + ->with('match_callback') + ->willReturn(null); + + $voter = new CallbackVoter(); + + $this->assertNull($voter->matchItem($item)); + } + + public function testMatchCallbackIsNotCallable(): void + { + $item = $this->createMock(ItemInterface::class); + $item->expects($this->once()) + ->method('getExtra') + ->with('match_callback') + ->willReturn('foo'); + + $voter = new CallbackVoter(); + + $this->expectException(\InvalidArgumentException::class); + + $voter->matchItem($item); + } + + /** + * @dataProvider provideData + */ + public function testMatching(callable $callback, ?bool $expected): void + { + $item = $this->createMock(ItemInterface::class); + $item->expects($this->once()) + ->method('getExtra') + ->with('match_callback') + ->willReturn($callback); + + $voter = new CallbackVoter(); + + $this->assertSame($expected, $voter->matchItem($item)); + } + + /** + * @return iterable + */ + public static function provideData(): iterable + { + yield 'matching' => [fn (): ?bool => true, true]; + yield 'not matching' => [fn (): ?bool => false, false]; + yield 'skipping' => [fn (): ?bool => null, null]; + } +}