Skip to content

Commit

Permalink
Merge pull request #387 from Florin1/add_callback_voter
Browse files Browse the repository at this point in the history
Add callback voter
  • Loading branch information
garak committed Oct 18, 2023
2 parents fbc03fb + 71f7e53 commit 3d5baa2
Show file tree
Hide file tree
Showing 4 changed files with 95 additions and 0 deletions.
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
## 3.5 (unreleased)

* Added CallbackVoter

## 3.4 (2023-05-17)

* Removed support for unsupported PHP version 7.4
Expand Down
1 change: 1 addition & 0 deletions doc/05-Matcher.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
----------------------
Expand Down
26 changes: 26 additions & 0 deletions src/Knp/Menu/Matcher/Voter/CallbackVoter.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
<?php

namespace Knp\Menu\Matcher\Voter;

use Knp\Menu\ItemInterface;

/**
* Voter based on a callback
*/
final class CallbackVoter implements VoterInterface
{
public function matchItem(ItemInterface $item): ?bool
{
$callback = $item->getExtra('match_callback');

if (null === $callback) {
return null;
}

if (!\is_callable($callback)) {
throw new \InvalidArgumentException('Extra "match_callback" must be callable.');
}

return $callback();
}
}
64 changes: 64 additions & 0 deletions tests/Knp/Menu/Tests/Matcher/Voter/CallbackVoterTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
<?php

namespace Knp\Menu\Tests\Matcher\Voter;

use Knp\Menu\ItemInterface;
use Knp\Menu\Matcher\Voter\CallbackVoter;
use PHPUnit\Framework\TestCase;

final class CallbackVoterTest extends TestCase
{
public function testNoMatchCallbackSet(): void
{
$item = $this->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<string, array{callable(): ?bool, ?bool}>
*/
public static function provideData(): iterable
{
yield 'matching' => [fn (): ?bool => true, true];
yield 'not matching' => [fn (): ?bool => false, false];
yield 'skipping' => [fn (): ?bool => null, null];
}
}

0 comments on commit 3d5baa2

Please sign in to comment.