Skip to content

Commit

Permalink
Add RouteVoter
Browse files Browse the repository at this point in the history
  • Loading branch information
msmakouz committed May 14, 2023
1 parent fc26503 commit 03d4bb1
Show file tree
Hide file tree
Showing 11 changed files with 341 additions and 34 deletions.
11 changes: 10 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,14 @@
# Changelog

## 1.0.0 - 2022-01-08
## Unreleased
- **Bug Fixes**
- **Medium Impact Changes**
- **Other Features**

## 1.1.0 - 2223-05-14
- **Other Features**
- Added `Spiral\KnpMenu\Matcher\Voter\RouteVoter`. It is added to the `Knp\Menu\Matcher\Matcher` by default.

## 1.0.0 - 2023-01-08

- initial release
9 changes: 5 additions & 4 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -19,11 +19,12 @@
"require-dev": {
"spiral/framework": "^3.5",
"roave/security-advisories": "dev-latest",
"phpunit/phpunit": "^9.5.27",
"phpunit/phpunit": "^10.1",
"friendsofphp/php-cs-fixer": "^3.8",
"spiral/testing": "^2.2.0",
"vimeo/psalm": "^4.30",
"spiral/twig-bridge": "^2.0"
"spiral/testing": "^2.3",
"vimeo/psalm": "^5.11",
"spiral/twig-bridge": "^2.0",
"spiral/nyholm-bridge": "^1.3"
},
"autoload": {
"psr-4": {
Expand Down
38 changes: 16 additions & 22 deletions phpunit.xml.dist
Original file line number Diff line number Diff line change
@@ -1,37 +1,31 @@
<?xml version="1.0" encoding="UTF-8"?>
<phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="https://schema.phpunit.de/9.5/phpunit.xsd"
backupGlobals="false"
colors="true"
verbose="true"
bootstrap="./vendor/autoload.php"
failOnRisky="true"
failOnWarning="true"
convertErrorsToExceptions="true"
convertNoticesToExceptions="true"
convertWarningsToExceptions="true"
stopOnFailure="false"
executionOrder="random"
resolveDependencies="true"
>
<phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="https://schema.phpunit.de/10.1/phpunit.xsd" backupGlobals="false" colors="true" bootstrap="./vendor/autoload.php" failOnRisky="true" failOnWarning="true" stopOnFailure="false" executionOrder="random" resolveDependencies="true" cacheDirectory=".phpunit.cache">
<php>
<ini name="display_errors" value="On" />
<ini name="error_reporting" value="-1" />
<ini name="memory_limit" value="-1" />
<ini name="display_errors" value="On"/>
<ini name="error_reporting" value="-1"/>
<ini name="memory_limit" value="-1"/>
</php>

<coverage>
<report>
<html outputDirectory="build/coverage"/>
<text outputFile="build/coverage.txt"/>
<clover outputFile="build/logs/clover.xml"/>
</report>
</coverage>
<logging>
<junit outputFile="build/report.junit.xml"/>
</logging>
<testsuites>
<testsuite name="Knp Menu Test Suite">
<directory>./tests/</directory>
</testsuite>
</testsuites>

<coverage>
<source>
<include>
<directory>src</directory>
</include>
<exclude>
<directory>./tests</directory>
</exclude>
</coverage>
</source>
</phpunit>
2 changes: 2 additions & 0 deletions psalm.xml
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="https://getpsalm.org/schema/config"
xsi:schemaLocation="https://getpsalm.org/schema/config vendor/vimeo/psalm/config.xsd"
findUnusedBaselineEntry="true"
findUnusedCode="false"
>
<projectFiles>
<directory name="src" />
Expand Down
35 changes: 30 additions & 5 deletions src/Bootloader/KnpMenuBootloader.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
use Knp\Menu\FactoryInterface;
use Knp\Menu\Matcher\Matcher;
use Knp\Menu\Matcher\MatcherInterface;
use Knp\Menu\Matcher\Voter\VoterInterface;
use Knp\Menu\MenuFactory;
use Knp\Menu\Provider\MenuProviderInterface;
use Knp\Menu\Renderer\PsrProvider;
Expand All @@ -17,8 +18,10 @@
use Spiral\Boot\DirectoriesInterface;
use Spiral\Config\ConfiguratorInterface;
use Spiral\Core\Container;
use Spiral\Core\Container\Autowire;
use Spiral\KnpMenu\Config\KnpMenuConfig;
use Spiral\KnpMenu\Extension\RoutingExtension;
use Spiral\KnpMenu\Matcher\Voter\RouteVoter;
use Spiral\KnpMenu\MenuInterface;
use Spiral\KnpMenu\MenuRegistry;
use Spiral\KnpMenu\Renderer\SpiralRenderer;
Expand All @@ -34,7 +37,7 @@ final class KnpMenuBootloader extends Bootloader
protected const SINGLETONS = [
FactoryInterface::class => MenuFactory::class,
MenuFactory::class => MenuFactory::class,
MatcherInterface::class => Matcher::class,
MatcherInterface::class => [self::class, 'initMatcher'],
MenuProviderInterface::class => [self::class, 'initMenuProvider'],
MenuRegistry::class => MenuProviderInterface::class,
RendererProviderInterface::class => [self::class, 'initRenderer'],
Expand Down Expand Up @@ -71,6 +74,9 @@ private function initConfig(): void
'template' => class_exists(TwigBootloader::class) ? 'knpMenu:knp_menu' : '',
'template_options' => [],
'menus' => [],
'voters' => [
RouteVoter::class
]
]
);
}
Expand All @@ -96,15 +102,34 @@ private function initMenuProvider(KnpMenuConfig $config, Container $container):
$registry = new MenuRegistry();

foreach ($config->getMenus() as $name => $menu) {
if (!$menu instanceof MenuInterface) {
$menu = $container->get($menu);
}

$menu = $this->wire($menu, $container);
\assert($menu instanceof MenuInterface);

$registry->add(array_is_list($config->getMenus()) ? $menu::class : $name, $menu);
}

return $registry;
}

private function initMatcher(KnpMenuConfig $config, Container $container): MatcherInterface
{
$voters = [];
foreach ($config->getVoters() as $voter) {
$voter = $this->wire($voter, $container);
\assert($voter instanceof VoterInterface);

$voters[] = $voter;
}

return new Matcher($voters);
}

private function wire(mixed $alias, Container $container): mixed
{
return match (true) {
\is_string($alias) => $container->make($alias),
$alias instanceof Autowire => $alias->resolve($container),
default => $alias
};
}
}
26 changes: 25 additions & 1 deletion src/Config/KnpMenuConfig.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,23 @@

namespace Spiral\KnpMenu\Config;

use Knp\Menu\Matcher\Voter\VoterInterface;
use Spiral\Core\Container\Autowire;
use Spiral\Core\InjectableConfig;
use Spiral\KnpMenu\Matcher\Voter\RouteVoter;
use Spiral\KnpMenu\MenuInterface;

/**
* @psalm-type TMenu = MenuInterface|class-string<MenuInterface>|Autowire<MenuInterface>
* @psalm-type TVoter = VoterInterface|class-string<VoterInterface>|Autowire<VoterInterface>
*
* @property array{
* template: non-empty-string,
* template_options: array<array-key, mixed>,
* menus: TMenu[],
* voters: TVoter[]
* } $config
*/
final class KnpMenuConfig extends InjectableConfig
{
public const CONFIG = 'knp-menu';
Expand All @@ -16,16 +29,27 @@ final class KnpMenuConfig extends InjectableConfig
'template' => '',
'template_options' => [],
'menus' => [],
'voters' => [
RouteVoter::class
]
];

/**
* @return array<MenuInterface|class-string|Autowire>
* @return TMenu[]
*/
public function getMenus(): array
{
return $this->config['menus'];
}

/**
* @return TVoter[]
*/
public function getVoters(): array
{
return $this->config['voters'];
}

/**
* @return non-empty-string
*/
Expand Down
4 changes: 4 additions & 0 deletions src/Extension/RoutingExtension.php
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,10 @@ public function buildOptions(array $options): array
$params = $options['routeParameters'] ?? [];

$options['uri'] = (string) $this->router->uri($options['route'], $params);

// adding the item route to the extras for the RouteVoter
$options['extras']['route'] = $options['route'];
$options['extras']['routeParameters'] = $params;
}

return $options;
Expand Down
45 changes: 45 additions & 0 deletions src/Matcher/Voter/RouteVoter.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
<?php

declare(strict_types=1);

namespace Spiral\KnpMenu\Matcher\Voter;

use Knp\Menu\ItemInterface;
use Knp\Menu\Matcher\Voter\VoterInterface;
use Spiral\Http\Request\InputManager;
use Spiral\Router\Router;

final class RouteVoter implements VoterInterface
{
public function __construct(
private readonly InputManager $request
) {
}

public function matchItem(ItemInterface $item): ?bool
{
$route = $this->request->attributes->get(Router::ROUTE_NAME);
$testedRoute = $item->getExtra('route');

if (null === $route || null === $testedRoute) {
return null;
}

if (!\is_string($testedRoute)) {
throw new \InvalidArgumentException('Route extra item must be string.');
}

if ($route !== $testedRoute) {
return false;
}

$parameters = $this->request->attributes->get(Router::ROUTE_MATCHES);
foreach ($item->getExtra('routeParameters', []) as $name => $testedParameter) {
if ((string) $parameters[$name] !== (string) $testedParameter) {
return false;
}
}

return true;
}
}
6 changes: 5 additions & 1 deletion tests/src/Functional/Bootloader/KnpMenuBootloaderTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
use Knp\Menu\Twig\MenuExtension;
use Spiral\Boot\DirectoriesInterface;
use Spiral\KnpMenu\Config\KnpMenuConfig;
use Spiral\KnpMenu\Matcher\Voter\RouteVoter;
use Spiral\KnpMenu\MenuRegistry;
use Spiral\KnpMenu\Renderer\SpiralRenderer;
use Spiral\KnpMenu\Tests\Functional\TestCase;
Expand Down Expand Up @@ -77,7 +78,10 @@ public function testDefaultConfigShouldBeDefined(): void
$this->assertConfigMatches(KnpMenuConfig::CONFIG, [
'template' => 'knpMenu:knp_menu',
'template_options' => [],
'menus' => []
'menus' => [],
'voters' => [
RouteVoter::class,
]
]);
}

Expand Down
4 changes: 4 additions & 0 deletions tests/src/Functional/TestCase.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,9 @@

namespace Spiral\KnpMenu\Tests\Functional;

use Spiral\Bootloader\Http\RouterBootloader;
use Spiral\KnpMenu\Bootloader\KnpMenuBootloader;
use Spiral\Nyholm\Bootloader\NyholmBootloader;
use Spiral\Twig\Bootloader\TwigBootloader;

abstract class TestCase extends \Spiral\Testing\TestCase
Expand All @@ -19,6 +21,8 @@ public function defineBootloaders(): array
return [
TwigBootloader::class,
KnpMenuBootloader::class,
RouterBootloader::class,
NyholmBootloader::class,
];
}
}
Loading

0 comments on commit 03d4bb1

Please sign in to comment.