-
-
Notifications
You must be signed in to change notification settings - Fork 2
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Use custom RouteParser wrapper to add fallback routes
Ref #1244
- Loading branch information
Showing
7 changed files
with
290 additions
and
31 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,103 @@ | ||
<?php | ||
|
||
declare(strict_types=1); | ||
|
||
/* | ||
* UserFrosting Core Sprinkle (http://www.userfrosting.com) | ||
* | ||
* @link https://github.com/userfrosting/sprinkle-core | ||
* @copyright Copyright (c) 2021 Alexander Weissman & Louis Charette | ||
* @license https://github.com/userfrosting/sprinkle-core/blob/master/LICENSE.md (MIT License) | ||
*/ | ||
|
||
namespace UserFrosting\Sprinkle\Core\Util; | ||
|
||
use Psr\Http\Message\UriInterface; | ||
use RuntimeException; | ||
use Slim\Interfaces\RouteCollectorInterface; | ||
|
||
/** | ||
* Wrapper around Slim's RouteParser. Allows to add 'fallback' routes when names | ||
* routes are not found. | ||
* | ||
* @see https://github.com/userfrosting/UserFrosting/issues/1244 | ||
*/ | ||
class RouteParser implements RouteParserInterface | ||
{ | ||
public function __construct( | ||
protected RouteCollectorInterface $routeCollector, | ||
) { | ||
} | ||
|
||
/** | ||
* {@inheritDoc} | ||
*/ | ||
public function relativeUrlFor( | ||
string $routeName, | ||
array $data = [], | ||
array $queryParams = [], | ||
?string $fallbackRoute = null, | ||
): string { | ||
try { | ||
$result = $this->routeCollector->getRouteParser()->relativeUrlFor($routeName, $data, $queryParams); | ||
} catch (RuntimeException $e) { | ||
if ($fallbackRoute !== null) { | ||
$result = $fallbackRoute; | ||
} else { | ||
throw $e; | ||
} | ||
} | ||
|
||
return $result; | ||
} | ||
|
||
/** | ||
* {@inheritDoc} | ||
*/ | ||
public function urlFor( | ||
string $routeName, | ||
array $data = [], | ||
array $queryParams = [], | ||
?string $fallbackRoute = null, | ||
): string { | ||
try { | ||
$result = $this->routeCollector->getRouteParser()->urlFor($routeName, $data, $queryParams); | ||
} catch (RuntimeException $e) { | ||
if ($fallbackRoute !== null) { | ||
$basePath = $this->routeCollector->getBasePath(); | ||
$result = $basePath . $fallbackRoute; | ||
} else { | ||
throw $e; | ||
} | ||
} | ||
|
||
return $result; | ||
} | ||
|
||
/** | ||
* {@inheritDoc} | ||
*/ | ||
public function fullUrlFor( | ||
UriInterface $uri, | ||
string $routeName, | ||
array $data = [], | ||
array $queryParams = [], | ||
?string $fallbackRoute = null, | ||
): string { | ||
try { | ||
$result = $this->routeCollector->getRouteParser()->fullUrlFor($uri, $routeName, $data, $queryParams); | ||
} catch (RuntimeException $e) { | ||
if ($fallbackRoute !== null) { | ||
$path = $this->urlFor($routeName, $data, $queryParams, $fallbackRoute); | ||
$scheme = $uri->getScheme(); | ||
$authority = $uri->getAuthority(); | ||
$protocol = ($scheme !== '' ? $scheme . ':' : '') . ($authority !== '' ? '//' . $authority : ''); | ||
$result = $protocol . $path; | ||
} else { | ||
throw $e; | ||
} | ||
} | ||
|
||
return $result; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,74 @@ | ||
<?php | ||
|
||
declare(strict_types=1); | ||
|
||
/* | ||
* UserFrosting Core Sprinkle (http://www.userfrosting.com) | ||
* | ||
* @link https://github.com/userfrosting/sprinkle-core | ||
* @copyright Copyright (c) 2021 Alexander Weissman & Louis Charette | ||
* @license https://github.com/userfrosting/sprinkle-core/blob/master/LICENSE.md (MIT License) | ||
*/ | ||
|
||
namespace UserFrosting\Sprinkle\Core\Util; | ||
|
||
use InvalidArgumentException; | ||
use Psr\Http\Message\UriInterface; | ||
use RuntimeException; | ||
use Slim\Interfaces\RouteParserInterface as SlimRouteParserInterface; | ||
|
||
interface RouteParserInterface extends SlimRouteParserInterface | ||
{ | ||
/** | ||
* Build the path for a named route excluding the base path | ||
* | ||
* @param string $routeName Route name | ||
* @param array<string, string> $data Named argument replacement data | ||
* @param array<string, string> $queryParams Optional query string parameters | ||
* @param string|null $fallbackRoute Optional fallback route (the actual route, not the route name) | ||
* | ||
* @throws RuntimeException If named route does not exist | ||
* @throws InvalidArgumentException If required data not provided | ||
*/ | ||
public function relativeUrlFor( | ||
string $routeName, | ||
array $data = [], | ||
array $queryParams = [], | ||
?string $fallbackRoute = null, | ||
): string; | ||
|
||
/** | ||
* Build the path for a named route including the base path | ||
* | ||
* @param string $routeName Route name | ||
* @param array<string, string> $data Named argument replacement data | ||
* @param array<string, string> $queryParams Optional query string parameters | ||
* @param string|null $fallbackRoute Optional fallback route (the actual route, not the route name) | ||
* | ||
* @throws RuntimeException If named route does not exist | ||
* @throws InvalidArgumentException If required data not provided | ||
*/ | ||
public function urlFor( | ||
string $routeName, | ||
array $data = [], | ||
array $queryParams = [], | ||
?string $fallbackRoute = null, | ||
): string; | ||
|
||
/** | ||
* Get fully qualified URL for named route | ||
* | ||
* @param UriInterface $uri | ||
* @param string $routeName Route name | ||
* @param array<string, string> $data Named argument replacement data | ||
* @param array<string, string> $queryParams Optional query string parameters | ||
* @param string|null $fallbackRoute Optional fallback route (the actual route, not the route name) | ||
*/ | ||
public function fullUrlFor( | ||
UriInterface $uri, | ||
string $routeName, | ||
array $data = [], | ||
array $queryParams = [], | ||
?string $fallbackRoute = null, | ||
): string; | ||
} |
27 changes: 0 additions & 27 deletions
27
app/tests/Integration/ServicesProvider/RoutingServiceTest.php
This file was deleted.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,102 @@ | ||
<?php | ||
|
||
declare(strict_types=1); | ||
|
||
/* | ||
* UserFrosting Core Sprinkle (http://www.userfrosting.com) | ||
* | ||
* @link https://github.com/userfrosting/sprinkle-core | ||
* @copyright Copyright (c) 2021 Alexander Weissman & Louis Charette | ||
* @license https://github.com/userfrosting/sprinkle-core/blob/master/LICENSE.md (MIT License) | ||
*/ | ||
|
||
namespace UserFrosting\Sprinkle\Core\Tests\Integration\Twig; | ||
|
||
use Mockery; | ||
use Mockery\Adapter\Phpunit\MockeryPHPUnitIntegration; | ||
use Psr\Http\Message\UriInterface; | ||
use Slim\App; | ||
use UserFrosting\Sprinkle\Core\Tests\CoreTestCase; | ||
use UserFrosting\Sprinkle\Core\Util\RouteParser; | ||
use UserFrosting\Sprinkle\Core\Util\RouteParserInterface; | ||
|
||
/** | ||
* Tests RoutesExtension. | ||
*/ | ||
class RouteParserTest extends CoreTestCase | ||
{ | ||
use MockeryPHPUnitIntegration; | ||
|
||
private RouteParser $parser; | ||
|
||
/** | ||
* Create parser manually to set basepath | ||
*/ | ||
public function setUp(): void | ||
{ | ||
parent::setUp(); | ||
|
||
/** @var App */ | ||
$app = $this->ci->get(App::class); | ||
$collector = $app->getRouteCollector(); | ||
$collector->setBasePath('/Myfoo'); | ||
|
||
$this->parser = new RouteParser($collector); | ||
} | ||
|
||
public function testRelativeUrlFor(): void | ||
{ | ||
// Valid | ||
$this->assertSame('/alerts', $this->parser->relativeUrlFor('alerts')); | ||
|
||
// Invalid, with fallback | ||
$this->assertSame('/fallback', $this->parser->relativeUrlFor('invalid', fallbackRoute: '/fallback')); | ||
|
||
// Invalid, no fallback | ||
$this->expectExceptionMessage('Named route does not exist for name: invalid'); | ||
$this->parser->relativeUrlFor('invalid'); | ||
} | ||
|
||
public function testUrlFor(): void | ||
{ | ||
// Valid | ||
$this->assertSame('/Myfoo/alerts', $this->parser->urlFor('alerts')); | ||
|
||
// Invalid, with fallback | ||
$this->assertSame('/Myfoo/fallback', $this->parser->urlFor('invalid', fallbackRoute: '/fallback')); | ||
|
||
// Invalid, no fallback | ||
$this->expectExceptionMessage('Named route does not exist for name: invalid'); | ||
$this->parser->urlFor('invalid'); | ||
} | ||
|
||
public function testFullUrlFor(): void | ||
{ | ||
/** @var UriInterface */ | ||
$uri = Mockery::mock(UriInterface::class) | ||
->shouldReceive('getScheme')->times(2)->andReturn('http') | ||
->shouldReceive('getAuthority')->times(2)->andReturn('localhost') | ||
->getMock(); | ||
|
||
// Valid | ||
$this->assertSame('http://localhost/Myfoo/alerts', $this->parser->fullUrlFor($uri, 'alerts')); | ||
|
||
// Invalid, with fallback | ||
$this->assertSame('http://localhost/Myfoo/fallback', $this->parser->fullUrlFor($uri, 'invalid', fallbackRoute: '/fallback')); | ||
|
||
// Invalid, no fallback | ||
$this->expectExceptionMessage('Named route does not exist for name: invalid'); | ||
$this->parser->fullUrlFor($uri, 'invalid'); | ||
} | ||
|
||
public function testService(): void | ||
{ | ||
/** @var RouteParserInterface */ | ||
$parser = $this->ci->get(RouteParserInterface::class); | ||
|
||
$this->assertSame('/alerts', $parser->relativeUrlFor('alerts')); | ||
$this->assertSame('/fallback', $parser->relativeUrlFor('invalid', fallbackRoute: '/fallback')); | ||
$this->expectExceptionMessage('Named route does not exist for name: invalid'); | ||
$parser->relativeUrlFor('invalid'); | ||
} | ||
} |