Skip to content

Commit a8b6ea9

Browse files
authored
feat(router): allow checking an action against the current route (#1059)
1 parent 1eaf65e commit a8b6ea9

File tree

4 files changed

+80
-0
lines changed

4 files changed

+80
-0
lines changed

src/Tempest/Router/src/GenericRouter.php

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
use Tempest\View\View;
2626

2727
use function Tempest\map;
28+
use function Tempest\Support\Regex\replace;
2829
use function Tempest\Support\str;
2930

3031
/**
@@ -173,6 +174,19 @@ public function toUri(array|string $action, ...$params): string
173174
return $uri->toString();
174175
}
175176

177+
public function isCurrentUri(array|string $action, ...$params): bool
178+
{
179+
$matchedRoute = $this->container->get(MatchedRoute::class);
180+
$candidateUri = $this->toUri($action, ...[...$matchedRoute->params, ...$params]);
181+
$currentUri = $this->toUri([$matchedRoute->route->handler->getDeclaringClass(), $matchedRoute->route->handler->getName()]);
182+
183+
foreach ($matchedRoute->params as $key => $value) {
184+
$currentUri = replace($currentUri, '/({' . preg_quote($key, '/') . '(?::.*?)?})/', $value);
185+
}
186+
187+
return $currentUri === $candidateUri;
188+
}
189+
176190
private function createResponse(Response|View $input): Response
177191
{
178192
if ($input instanceof View) {

src/Tempest/Router/src/Router.php

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,16 @@ interface Router
1010
{
1111
public function dispatch(Request|PsrRequest $request): Response;
1212

13+
/**
14+
* Creates a valid URI to the given `$action`.
15+
*/
1316
public function toUri(array|string $action, ...$params): string;
1417

18+
/**
19+
* Checks if the URI to the given `$action` would match the current route.
20+
*/
21+
public function isCurrentUri(array|string $action, ...$params): bool;
22+
1523
/**
1624
* @template T of \Tempest\Router\HttpMiddleware
1725
* @param class-string<T> $middlewareClass

src/Tempest/Router/src/functions.php

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,4 +25,24 @@ function uri(array|string|MethodReflector $action, mixed ...$params): string
2525
...$params,
2626
);
2727
}
28+
29+
/**
30+
* Checks whether the given controller action matches the current URI.
31+
*/
32+
function is_current_uri(array|string|MethodReflector $action, mixed ...$params): bool
33+
{
34+
if ($action instanceof MethodReflector) {
35+
$action = [
36+
$action->getDeclaringClass()->getName(),
37+
$action->getName(),
38+
];
39+
}
40+
41+
$router = get(Router::class);
42+
43+
return $router->isCurrentUri(
44+
$action,
45+
...$params,
46+
);
47+
}
2848
}

tests/Integration/Route/RouterTest.php

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
use Tempest\Database\Migrations\CreateMigrationsTable;
1212
use Tempest\Http\Status;
1313
use Tempest\Router\GenericRouter;
14+
use Tempest\Router\MatchedRoute;
1415
use Tempest\Router\Responses\Ok;
1516
use Tempest\Router\Router;
1617
use Tests\Tempest\Fixtures\Controllers\ControllerWithEnumBinding;
@@ -209,4 +210,41 @@ public function test_json_request(): void
209210
$this->assertSame(Status::OK, $response->status);
210211
$this->assertSame('foo', $response->body);
211212
}
213+
214+
public function test_is_current_uri(): void
215+
{
216+
$router = $this->container->get(GenericRouter::class);
217+
218+
$this->http->get('/test')->assertOk();
219+
220+
$this->assertTrue($router->isCurrentUri([TestController::class, '__invoke']));
221+
$this->assertFalse($router->isCurrentUri([TestController::class, 'withParams']));
222+
$this->assertFalse($router->isCurrentUri([TestController::class, 'withParams'], id: 1));
223+
$this->assertFalse($router->isCurrentUri([TestController::class, 'withParams'], id: 1, name: 'a'));
224+
}
225+
226+
public function test_is_current_uri_with_constrained_parameters(): void
227+
{
228+
$router = $this->container->get(GenericRouter::class);
229+
230+
$this->http->get('/test/1/a')->assertOk();
231+
232+
$this->assertTrue($router->isCurrentUri([TestController::class, 'withCustomRegexParams']));
233+
$this->assertTrue($router->isCurrentUri([TestController::class, 'withCustomRegexParams'], id: 1));
234+
$this->assertTrue($router->isCurrentUri([TestController::class, 'withCustomRegexParams'], id: 1, name: 'a'));
235+
$this->assertFalse($router->isCurrentUri([TestController::class, 'withCustomRegexParams'], id: 1, name: 'b'));
236+
$this->assertFalse($router->isCurrentUri([TestController::class, 'withCustomRegexParams'], id: 0, name: 'a'));
237+
$this->assertFalse($router->isCurrentUri([TestController::class, 'withCustomRegexParams'], id: 0, name: 'b'));
238+
}
239+
240+
public function test_is_current_uri_with_enum(): void
241+
{
242+
$router = $this->container->get(GenericRouter::class);
243+
244+
$this->http->get('/with-enum/foo')->assertOk();
245+
246+
$this->assertTrue($router->isCurrentUri(ControllerWithEnumBinding::class));
247+
$this->assertTrue($router->isCurrentUri(ControllerWithEnumBinding::class, input: EnumForController::FOO));
248+
$this->assertFalse($router->isCurrentUri(ControllerWithEnumBinding::class, input: EnumForController::BAR));
249+
}
212250
}

0 commit comments

Comments
 (0)