From ac208fb3ca09511f8d392d48da4bd281575ef868 Mon Sep 17 00:00:00 2001 From: Nuno Maduro Date: Wed, 16 Aug 2023 14:51:24 +0100 Subject: [PATCH 1/8] Fixes `route()` base uri, query parameters and domain --- src/Console/ListCommand.php | 2 +- src/FolioRoutes.php | 78 ++++++++++++++----- tests/Feature/NameTest.php | 20 +++++ .../views/even-more-pages/profile.blade.php | 4 + tests/Unit/FolioRoutesTest.php | 68 +++++++++++++++- 5 files changed, 149 insertions(+), 23 deletions(-) diff --git a/src/Console/ListCommand.php b/src/Console/ListCommand.php index cfe6b55..c9c1203 100644 --- a/src/Console/ListCommand.php +++ b/src/Console/ListCommand.php @@ -152,7 +152,7 @@ protected function routesFromMountPaths(array $mountPaths): Collection protected function routeName(string $mountPath, string $viewPath): ?string { return collect($this->laravel->make(FolioRoutes::class)->routes())->search(function (array $route) use ($mountPath, $viewPath) { - [$routeRelativeMountPath, $routeRelativeViewPath] = $route; + ['mountPath' => $routeRelativeMountPath, 'path' => $routeRelativeViewPath] = $route; return $routeRelativeMountPath === Project::relativePathOf($mountPath) && $routeRelativeViewPath === Project::relativePathOf($viewPath); diff --git a/src/FolioRoutes.php b/src/FolioRoutes.php index b2c314b..d49df92 100644 --- a/src/FolioRoutes.php +++ b/src/FolioRoutes.php @@ -16,6 +16,11 @@ class FolioRoutes { + /** + * The current version of the persisted routes. + */ + protected static int $version = 1; + /** * Create a new Folio routes instance. * @@ -41,7 +46,10 @@ public function persist(): void File::put( $this->cachedFolioRoutesPath, - 'routes, true).';', + ' static::$version, + 'routes' => $this->routes, + ], true).';', ); } @@ -67,9 +75,15 @@ protected function load(): void } if (File::exists($this->cachedFolioRoutesPath)) { - $this->routes = File::getRequire($this->cachedFolioRoutesPath); + $cache = File::getRequire($this->cachedFolioRoutesPath); - return; + if (isset($cache['version']) && (int) $cache['version'] === static::$version) { + $this->routes = $cache['routes']; + + $this->loaded = true; + + return; + } } foreach ($this->manager->mountPaths() as $mountPath) { @@ -80,8 +94,10 @@ protected function load(): void if ($name = $matchedView->name()) { $this->routes[$name] = [ - Project::relativePathOf($matchedView->mountPath), - Project::relativePathOf($matchedView->path), + 'mountPath' => Project::relativePathOf($matchedView->mountPath), + 'path' => Project::relativePathOf($matchedView->path), + 'baseUri' => $mountPath->baseUri, + 'domain' => $mountPath->domain, ]; } } @@ -102,6 +118,8 @@ public function has(string $name): bool /** * Get the route URL for the given route name and arguments. + * + * @thows \Laravel\Folio\Exceptions\UrlGenerationException */ public function get(string $name, array $arguments, bool $absolute): string { @@ -111,42 +129,61 @@ public function get(string $name, array $arguments, bool $absolute): string throw new RouteNotFoundException("Route [{$name}] not found."); } - [$mountPath, $path] = $this->routes[$name]; + [ + 'mountPath' => $mountPath, + 'path' => $path, + 'baseUri' => $baseUri, + 'domain' => $domain, + ] = $this->routes[$name]; - return with($this->path($mountPath, $path, $arguments), function (string $path) use ($absolute) { - return $absolute ? url($path) : $path; - }); + [$path, $remainingArguments] = $this->path($mountPath, $path, $arguments); + + $route = new Route(['GET'], '{__folio_path}', fn () => null); + + $route->name($name)->domain($domain); + + $uri = $baseUri === '/' ? $path : $baseUri.$path; + + try { + return url()->toRoute($route, [...$remainingArguments, '__folio_path' => $uri], $absolute); + } catch (\Illuminate\Routing\Exceptions\UrlGenerationException $e) { + throw new UrlGenerationException(str_replace('{__folio_path}', $uri, $e->getMessage()), $e->getCode(), $e); + } } /** * Get the relative route URL for the given route name and arguments. * * @param array $parameters + * @return array{string, array} */ - protected function path(string $mountPath, string $path, array $parameters): string + protected function path(string $mountPath, string $path, array $parameters): array { $uri = str_replace('.blade.php', '', $path); + $parameters = collect($parameters); + $usedParameters = collect(); + $uri = collect(explode('/', $uri)) - ->map(function (string $segment) use ($parameters, $uri) { + ->map(function (string $segment) use ($parameters, $uri, $usedParameters) { if (! Str::startsWith($segment, '[')) { return $segment; } $segment = new PotentiallyBindablePathSegment($segment); - $parameters = collect($parameters)->mapWithKeys(function (mixed $value, string $key) { - return [Str::camel($key) => $value]; - })->all(); + $name = $segment->variable(); - if (! isset($parameters[$name = $segment->variable()]) || $parameters[$name] === null) { - throw UrlGenerationException::forMissingParameter($uri, $name); - } + $value = $parameters->first(function (mixed $value, string $key) use ($name) { + return Str::camel($key) === $name && $value !== null; + }, fn () => throw UrlGenerationException::forMissingParameter($uri, $name)); + + $usedParameters->put($name, $value); return $this->formatParameter( $uri, $name, - $parameters[$name], + $value, $segment->field(), $segment->capturesMultipleSegments() ); @@ -154,7 +191,10 @@ protected function path(string $mountPath, string $path, array $parameters): str $uri = str_replace(['/index', '/index/'], ['', '/'], $uri); - return '/'.ltrim(substr($uri, strlen($mountPath)), '/'); + return [ + '/'.ltrim(substr($uri, strlen($mountPath)), '/'), + $parameters->except($usedParameters->keys()->all())->all(), + ]; } /** diff --git a/tests/Feature/NameTest.php b/tests/Feature/NameTest.php index 5427848..6511a14 100644 --- a/tests/Feature/NameTest.php +++ b/tests/Feature/NameTest.php @@ -115,3 +115,23 @@ test('routes may not have a name', function () { route('users.index'); })->throws(RouteNotFoundException::class, 'Route [users.index] not defined.'); + +test('custom uri', function () { + Folio::uri('/user')->path(__DIR__.'/resources/views/even-more-pages'); + + $absoluteRoute = route('profile'); + $route = route('profile', [], false); + + expect($absoluteRoute)->toBe('http://localhost/user/profile') + ->and($route)->toBe('/user/profile'); +}); + +test('custom domain', function () { + Folio::domain('example.com')->uri('/user')->path(__DIR__.'/resources/views/even-more-pages'); + + $absoluteRoute = route('profile'); + $route = route('profile', [], false); + + expect($absoluteRoute)->toBe('http://example.com/user/profile') + ->and($route)->toBe('/user/profile'); +}); diff --git a/tests/Feature/resources/views/even-more-pages/profile.blade.php b/tests/Feature/resources/views/even-more-pages/profile.blade.php index a73172a..a75993b 100644 --- a/tests/Feature/resources/views/even-more-pages/profile.blade.php +++ b/tests/Feature/resources/views/even-more-pages/profile.blade.php @@ -1,3 +1,7 @@
My profile
+ +map(fn ($argument) => value($argument))->all(); $names = new FolioRoutes(Mockery::mock(FolioManager::class), '', [ - $name => [$mountPath, $viewPath], + $name => [ + 'mountPath' => $mountPath, + 'path' => $viewPath, + 'baseUri' => '/', + 'domain' => null, + ], ], true); expect($names->has($name))->toBeTrue() ->and($names->get($name, $arguments, false))->toBe($expectedRoute); })->with(fn () => collect([ 'podcasts.index' => ['podcasts/index.blade.php', [], '/podcasts'], + 'podcasts.index-with-query-parameters' => ['podcasts/index.blade.php', ['page' => 1], '/podcasts?page=1'], 'podcasts.show-by-id' => ['podcasts/[id].blade.php', ['id' => 1], '/podcasts/1'], 'podcasts.show-by-name' => ['podcasts/[name].blade.php', ['name' => 'Taylor'], '/podcasts/Taylor'], 'podcasts.show-by-slug' => ['podcasts/[slug].blade.php', ['slug' => 'nuno'], '/podcasts/nuno'], @@ -49,6 +55,7 @@ 'podcasts.show-by-backed-enum' => ['podcasts/[Category].blade.php', ['category' => Category::Post], '/podcasts/posts'], 'podcasts.show-by-id-with-nested-page' => ['podcasts/[id]/stats.blade.php', ['id' => 1], '/podcasts/1/stats'], 'podcasts.stats' => ['podcasts/stats.blade.php', [], '/podcasts/stats'], + 'podcasts.stats-with-query-parameters' => ['podcasts/stats.blade.php', ['page' => 1, 'lowerCase' => 'lowerCaseKeyValue', 'Upper_case-key' => 'Upper_caseKeyValue'], '/podcasts/stats?page=1&lowerCase=lowerCaseKeyValue&Upper_case-key=Upper_caseKeyValue'], 'podcasts.many-by-id' => ['podcasts/[...id].blade.php', ['ids' => [1, 2, 3]], '/podcasts/1/2/3'], 'podcasts.many-by-name' => ['podcasts/[...name].blade.php', ['names' => ['Taylor', 'Nuno']], '/podcasts/Taylor/Nuno'], 'podcasts.many-by-slug' => ['podcasts/[...slug].blade.php', ['slugs' => ['nuno', 'taylor']], '/podcasts/nuno/taylor'], @@ -59,6 +66,9 @@ 'podcasts.many-by-model-name-2' => ['podcasts/[...Podcast-name].blade.php', ['podcasts' => fn () => Podcast::all()], '/podcasts/test-podcast-name-1/test-podcast-name-2'], 'podcasts.many-by-backed-enum' => ['podcasts/[...Category].blade.php', ['categories' => [Category::Post, Category::Video]], '/podcasts/posts/video'], 'podcasts.many-by-id-with-nested-page' => ['podcasts/[...id]/stats.blade.php', ['ids' => [1, 2, 3]], '/podcasts/1/2/3/stats'], + 'articles.query-parameter' => ['articles.blade.php', ['page' => 1], '/articles?page=1'], + 'articles.query-parameters' => ['articles.blade.php', ['page' => 1, 'lowerCase' => 'lowerCaseKeyValue', 'Upper_case-key' => 'Upper_caseKeyValue'], '/articles?page=1&lowerCase=lowerCaseKeyValue&Upper_case-key=Upper_caseKeyValue'], + 'articles.query-array-parameter' => ['articles.blade.php', ['page' => [1, 2]], '/articles?page%5B0%5D=1&page%5B1%5D=2'], ])->map(function (array $value) { $mountPath = 'resources/views/pages'; @@ -67,9 +77,56 @@ return [$mountPath, $mountPath.'/'.$viewRelativePath, $arguments, $expectedRoute]; })->mapWithKeys(fn (array $value, string $key) => [$key => [$key, $value]])->toArray()); +it('may have absolute routes', function (string $name, array $scenario) { + [$mountPath, $viewPath, $domain, $arguments, $expectedRoute] = $scenario; + + $arguments = collect($arguments)->map(fn ($argument) => value($argument))->all(); + + $names = new FolioRoutes(Mockery::mock(FolioManager::class), '', [ + $name => [ + 'mountPath' => $mountPath, + 'path' => $viewPath, + 'baseUri' => '/', + 'domain' => $domain, + ], + ], true); + + expect($names->has($name))->toBeTrue() + ->and($names->get($name, $arguments, true))->toBe($expectedRoute); +})->with(fn () => collect([ + 'podcasts.index' => ['podcasts/index.blade.php', 'domain.com', [], 'http://domain.com/podcasts'], + 'podcasts.show' => ['podcasts/[id].blade.php', 'domain.com', ['id' => 1], 'http://domain.com/podcasts/1'], + 'podcasts.show-by-account-and-name' => ['podcasts/[name].blade.php', '{account}.domain.com', ['account' => 'taylor', 'name' => 'Taylor'], 'http://taylor.domain.com/podcasts/Taylor'], +])->map(function (array $value) { + $mountPath = 'resources/views/pages'; + + [$viewRelativePath, $domain, $arguments, $expectedRoute] = $value; + + return [$mountPath, $mountPath.'/'.$viewRelativePath, $domain, $arguments, $expectedRoute]; +})->mapWithKeys(fn (array $value, string $key) => [$key => [$key, $value]])->toArray()); + +test('precedence', function () { + $names = new FolioRoutes(Mockery::mock(FolioManager::class), '', [ + 'podcasts.show' => [ + 'mountPath' => 'resources/views/pages', + 'path' => 'resources/views/pages/podcasts/[id].blade.php', + 'baseUri' => '/', + 'domain' => null, + ], + ], true); + + expect(fn () => $names->get('podcasts.show', ['id' => '{name}', 'name' => 'foo'], false)) + ->toThrow(UrlGenerationException::class, 'Missing required parameter for [Route: podcasts.show] [URI: /podcasts/{name}] [Missing parameter: name].'); +}); + it('may not have routes', function () { $names = new FolioRoutes(Mockery::mock(FolioManager::class), '', [ - 'podcasts.index' => ['resources/views/pages', 'resources/views/pages/podcasts/index.blade.php'], + 'podcasts.index' => [ + 'mountPath' => 'resources/views/pages', + 'path' => 'resources/views/pages/podcasts/index.blade.php', + 'baseUri' => '/', + 'domain' => null, + ], ], true); expect($names->has('podcasts.show'))->toBeFalse() @@ -78,7 +135,12 @@ it('can not have missing parameters', function () { $names = new FolioRoutes(Mockery::mock(FolioManager::class), '', [ - 'podcasts.show' => ['resources/views/pages', 'resources/views/pages/podcasts/[id].blade.php'], + 'podcasts.show' => [ + 'mountPath' => 'resources/views/pages', + 'path' => 'resources/views/pages/podcasts/[id].blade.php', + 'baseUri' => '/', + 'domain' => null, + ], ], true); expect(fn () => $names->get('podcasts.show', [], false)) From 84d7563560cfd27c40e20bab9290b0fe441ae7c6 Mon Sep 17 00:00:00 2001 From: nunomaduro Date: Wed, 16 Aug 2023 13:51:48 +0000 Subject: [PATCH 2/8] Fix code styling --- src/Options/PageOptions.php | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Options/PageOptions.php b/src/Options/PageOptions.php index 6fd687f..65275a5 100644 --- a/src/Options/PageOptions.php +++ b/src/Options/PageOptions.php @@ -3,6 +3,7 @@ namespace Laravel\Folio\Options; use Closure; + use function Laravel\Folio\middleware; use function Laravel\Folio\name; use function Laravel\Folio\withTrashed; From c2aed553137f2dfadf84b1e74991cf662fd74641 Mon Sep 17 00:00:00 2001 From: Nuno Maduro Date: Wed, 16 Aug 2023 15:24:33 +0100 Subject: [PATCH 3/8] More tests --- src/FolioRoutes.php | 16 ++++++---- tests/Unit/FolioRoutesTest.php | 54 ++++++++++++++++++---------------- 2 files changed, 39 insertions(+), 31 deletions(-) diff --git a/src/FolioRoutes.php b/src/FolioRoutes.php index d49df92..a1ef623 100644 --- a/src/FolioRoutes.php +++ b/src/FolioRoutes.php @@ -174,16 +174,20 @@ protected function path(string $mountPath, string $path, array $parameters): arr $name = $segment->variable(); - $value = $parameters->first(function (mixed $value, string $key) use ($name) { + $key = $parameters->search(function (mixed $value, string $key) use ($name) { return Str::camel($key) === $name && $value !== null; - }, fn () => throw UrlGenerationException::forMissingParameter($uri, $name)); + }); - $usedParameters->put($name, $value); + if ($key === false) { + throw UrlGenerationException::forMissingParameter($uri, $name); + } + + $usedParameters->add($key); return $this->formatParameter( $uri, - $name, - $value, + Str::camel($key), + $parameters->get($key), $segment->field(), $segment->capturesMultipleSegments() ); @@ -193,7 +197,7 @@ protected function path(string $mountPath, string $path, array $parameters): arr return [ '/'.ltrim(substr($uri, strlen($mountPath)), '/'), - $parameters->except($usedParameters->keys()->all())->all(), + $parameters->except($usedParameters->all())->all(), ]; } diff --git a/tests/Unit/FolioRoutesTest.php b/tests/Unit/FolioRoutesTest.php index cee803b..82be9d4 100644 --- a/tests/Unit/FolioRoutesTest.php +++ b/tests/Unit/FolioRoutesTest.php @@ -45,7 +45,7 @@ 'podcasts.index' => ['podcasts/index.blade.php', [], '/podcasts'], 'podcasts.index-with-query-parameters' => ['podcasts/index.blade.php', ['page' => 1], '/podcasts?page=1'], 'podcasts.show-by-id' => ['podcasts/[id].blade.php', ['id' => 1], '/podcasts/1'], - 'podcasts.show-by-name' => ['podcasts/[name].blade.php', ['name' => 'Taylor'], '/podcasts/Taylor'], + 'podcasts.show-by-name' => ['podcasts/[name].blade.php', ['Name' => 'Taylor'], '/podcasts/Taylor'], 'podcasts.show-by-slug' => ['podcasts/[slug].blade.php', ['slug' => 'nuno'], '/podcasts/nuno'], 'podcasts.show-by-slug-and-id' => ['podcasts/[slug]/[id].blade.php', ['slug' => 'nuno', 'id' => 1], '/podcasts/nuno/1'], 'podcasts.show-by-model' => ['podcasts/[Podcast].blade.php', ['podcast' => fn () => Podcast::first()], '/podcasts/1'], @@ -55,11 +55,11 @@ 'podcasts.show-by-backed-enum' => ['podcasts/[Category].blade.php', ['category' => Category::Post], '/podcasts/posts'], 'podcasts.show-by-id-with-nested-page' => ['podcasts/[id]/stats.blade.php', ['id' => 1], '/podcasts/1/stats'], 'podcasts.stats' => ['podcasts/stats.blade.php', [], '/podcasts/stats'], - 'podcasts.stats-with-query-parameters' => ['podcasts/stats.blade.php', ['page' => 1, 'lowerCase' => 'lowerCaseKeyValue', 'Upper_case-key' => 'Upper_caseKeyValue'], '/podcasts/stats?page=1&lowerCase=lowerCaseKeyValue&Upper_case-key=Upper_caseKeyValue'], + 'podcasts.stats-with-query-parameters' => ['podcasts/stats.blade.php', ['Page' => 1, 'lowerCase' => 'lowerCaseKeyValue', 'Upper_case-key' => 'Upper_caseKeyValue'], '/podcasts/stats?Page=1&lowerCase=lowerCaseKeyValue&Upper_case-key=Upper_caseKeyValue'], 'podcasts.many-by-id' => ['podcasts/[...id].blade.php', ['ids' => [1, 2, 3]], '/podcasts/1/2/3'], 'podcasts.many-by-name' => ['podcasts/[...name].blade.php', ['names' => ['Taylor', 'Nuno']], '/podcasts/Taylor/Nuno'], 'podcasts.many-by-slug' => ['podcasts/[...slug].blade.php', ['slugs' => ['nuno', 'taylor']], '/podcasts/nuno/taylor'], - 'podcasts.many-by-slug-and-id' => ['podcasts/[...slug]/[...id].blade.php', ['slugs' => ['nuno', 'taylor'], 'ids' => [1, 2]], '/podcasts/nuno/taylor/1/2'], + 'podcasts.many-by-slug-and-id' => ['podcasts/[...slug]/[...id].blade.php', ['Slugs' => ['nuno', 'taylor'], 'ids' => [1, 2]], '/podcasts/nuno/taylor/1/2'], 'podcasts.many-by-model' => ['podcasts/[...Podcast].blade.php', ['podcasts' => fn () => Podcast::all()], '/podcasts/1/2'], 'podcasts.many-by-model-fqn' => ['podcasts/[...Tests.Feature.Fixtures.Podcast].blade.php', ['podcasts' => fn () => Podcast::all()], '/podcasts/1/2'], 'podcasts.many-by-model-name-1' => ['podcasts/[...Podcast:name].blade.php', ['podcasts' => fn () => Podcast::all()], '/podcasts/test-podcast-name-1/test-podcast-name-2'], @@ -97,6 +97,7 @@ 'podcasts.index' => ['podcasts/index.blade.php', 'domain.com', [], 'http://domain.com/podcasts'], 'podcasts.show' => ['podcasts/[id].blade.php', 'domain.com', ['id' => 1], 'http://domain.com/podcasts/1'], 'podcasts.show-by-account-and-name' => ['podcasts/[name].blade.php', '{account}.domain.com', ['account' => 'taylor', 'name' => 'Taylor'], 'http://taylor.domain.com/podcasts/Taylor'], + 'podcasts.show-by-account-and-name-and-pagination' => ['podcasts/[name].blade.php', '{account}.domain.com', ['account' => 'taylor', 'name' => 'Taylor', 'page' => 1], 'http://taylor.domain.com/podcasts/Taylor?page=1'], ])->map(function (array $value) { $mountPath = 'resources/views/pages'; @@ -105,19 +106,36 @@ return [$mountPath, $mountPath.'/'.$viewRelativePath, $domain, $arguments, $expectedRoute]; })->mapWithKeys(fn (array $value, string $key) => [$key => [$key, $value]])->toArray()); -test('precedence', function () { +test('missing parameters', function (string $name, array $scenario) { + [$mountPath, $viewPath, $domain, $arguments, $expectedExpectationMessage] = $scenario; + + $arguments = collect($arguments)->map(fn ($argument) => value($argument))->all(); + $names = new FolioRoutes(Mockery::mock(FolioManager::class), '', [ - 'podcasts.show' => [ - 'mountPath' => 'resources/views/pages', - 'path' => 'resources/views/pages/podcasts/[id].blade.php', + $name => [ + 'mountPath' => $mountPath, + 'path' => $viewPath, 'baseUri' => '/', - 'domain' => null, + 'domain' => $domain, ], ], true); - expect(fn () => $names->get('podcasts.show', ['id' => '{name}', 'name' => 'foo'], false)) - ->toThrow(UrlGenerationException::class, 'Missing required parameter for [Route: podcasts.show] [URI: /podcasts/{name}] [Missing parameter: name].'); -}); + expect($names->has($name))->toBeTrue() + ->and(fn () => $names->get($name, $arguments, true))->toThrow( + UrlGenerationException::class, + $expectedExpectationMessage, + ); +})->with(fn () => collect([ + 'podcasts.show' => ['podcasts/[id].blade.php', 'domain.com', [], 'Missing required parameter [id] for path [resources/views/pages/podcasts/[id]].'], + 'podcasts.show-by-account-and-name' => ['podcasts/[name].blade.php', '{account}.domain.com', ['name' => 'Taylor'], 'Missing required parameter for [Route: podcasts.show-by-account-and-name] [URI: /podcasts/Taylor] [Missing parameter: account].'], + 'podcasts.show-by-account-and-name-and-pagination' => ['podcasts/[name].blade.php', '{account}.domain.com', ['account' => 'taylor', 'page' => 1], 'Missing required parameter [name] for path [resources/views/pages/podcasts/[name]].'], +])->map(function (array $value) { + $mountPath = 'resources/views/pages'; + + [$viewRelativePath, $domain, $arguments, $expectedExpectationMessage] = $value; + + return [$mountPath, $mountPath.'/'.$viewRelativePath, $domain, $arguments, $expectedExpectationMessage]; +})->mapWithKeys(fn (array $value, string $key) => [$key => [$key, $value]])->toArray()); it('may not have routes', function () { $names = new FolioRoutes(Mockery::mock(FolioManager::class), '', [ @@ -132,17 +150,3 @@ expect($names->has('podcasts.show'))->toBeFalse() ->and(fn () => $names->get('podcasts.show', [], false))->toThrow(RouteNotFoundException::class); }); - -it('can not have missing parameters', function () { - $names = new FolioRoutes(Mockery::mock(FolioManager::class), '', [ - 'podcasts.show' => [ - 'mountPath' => 'resources/views/pages', - 'path' => 'resources/views/pages/podcasts/[id].blade.php', - 'baseUri' => '/', - 'domain' => null, - ], - ], true); - - expect(fn () => $names->get('podcasts.show', [], false)) - ->toThrow(UrlGenerationException::class, 'Missing required parameter [id] for path [resources/views/pages/podcasts/[id]].'); -}); From 4586793882c9fb1d7052085b98af0b42b3358913 Mon Sep 17 00:00:00 2001 From: Nuno Maduro Date: Wed, 16 Aug 2023 15:32:43 +0100 Subject: [PATCH 4/8] More tests --- src/Options/PageOptions.php | 1 - tests/Unit/FolioRoutesTest.php | 2 ++ 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/src/Options/PageOptions.php b/src/Options/PageOptions.php index 65275a5..6fd687f 100644 --- a/src/Options/PageOptions.php +++ b/src/Options/PageOptions.php @@ -3,7 +3,6 @@ namespace Laravel\Folio\Options; use Closure; - use function Laravel\Folio\middleware; use function Laravel\Folio\name; use function Laravel\Folio\withTrashed; diff --git a/tests/Unit/FolioRoutesTest.php b/tests/Unit/FolioRoutesTest.php index 82be9d4..0246b94 100644 --- a/tests/Unit/FolioRoutesTest.php +++ b/tests/Unit/FolioRoutesTest.php @@ -129,6 +129,8 @@ 'podcasts.show' => ['podcasts/[id].blade.php', 'domain.com', [], 'Missing required parameter [id] for path [resources/views/pages/podcasts/[id]].'], 'podcasts.show-by-account-and-name' => ['podcasts/[name].blade.php', '{account}.domain.com', ['name' => 'Taylor'], 'Missing required parameter for [Route: podcasts.show-by-account-and-name] [URI: /podcasts/Taylor] [Missing parameter: account].'], 'podcasts.show-by-account-and-name-and-pagination' => ['podcasts/[name].blade.php', '{account}.domain.com', ['account' => 'taylor', 'page' => 1], 'Missing required parameter [name] for path [resources/views/pages/podcasts/[name]].'], + 'podcasts.show-by-account-and-account' => ['podcasts/[account].blade.php', '{account}.domain.com', ['account' => 'taylor'], 'Missing required parameter for [Route: podcasts.show-by-account-and-account] [URI: /podcasts/taylor] [Missing parameter: account].'], + 'podcasts.show-by-account-and-{name}' => ['podcasts/name.blade.php', '{account}.domain.com', ['account' => '{name}', 'name' => 'nuno'], 'Missing required parameter for [Route: podcasts.show-by-account-and-{name}] [URI: /podcasts/name] [Missing parameter: name].'], ])->map(function (array $value) { $mountPath = 'resources/views/pages'; From ab93cac3faecebecd1a25ebcb6094fa0ca654fe8 Mon Sep 17 00:00:00 2001 From: nunomaduro Date: Wed, 16 Aug 2023 14:33:13 +0000 Subject: [PATCH 5/8] Fix code styling --- src/Options/PageOptions.php | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Options/PageOptions.php b/src/Options/PageOptions.php index 6fd687f..65275a5 100644 --- a/src/Options/PageOptions.php +++ b/src/Options/PageOptions.php @@ -3,6 +3,7 @@ namespace Laravel\Folio\Options; use Closure; + use function Laravel\Folio\middleware; use function Laravel\Folio\name; use function Laravel\Folio\withTrashed; From c53e8ec72e107960df38ae3b05e210f0eede2207 Mon Sep 17 00:00:00 2001 From: Nuno Maduro Date: Wed, 16 Aug 2023 15:45:55 +0100 Subject: [PATCH 6/8] And more tests --- tests/Unit/FolioRoutesTest.php | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/tests/Unit/FolioRoutesTest.php b/tests/Unit/FolioRoutesTest.php index 0246b94..b3f828d 100644 --- a/tests/Unit/FolioRoutesTest.php +++ b/tests/Unit/FolioRoutesTest.php @@ -106,6 +106,34 @@ return [$mountPath, $mountPath.'/'.$viewRelativePath, $domain, $arguments, $expectedRoute]; })->mapWithKeys(fn (array $value, string $key) => [$key => [$key, $value]])->toArray()); +it('may have routes with custom base uri', function (string $name, array $scenario) { + [$mountPath, $viewPath, $baseUri, $arguments, $expectedRoute] = $scenario; + + $arguments = collect($arguments)->map(fn ($argument) => value($argument))->all(); + + $names = new FolioRoutes(Mockery::mock(FolioManager::class), '', [ + $name => [ + 'mountPath' => $mountPath, + 'path' => $viewPath, + 'baseUri' => $baseUri, + 'domain' => null, + ], + ], true); + + expect($names->has($name))->toBeTrue() + ->and($names->get($name, $arguments, true))->toBe($expectedRoute); +})->with(fn () => collect([ + 'podcasts.index' => ['podcasts/index.blade.php', '/a', [], 'http://localhost/a/podcasts'], + 'podcasts.show' => ['podcasts/[id].blade.php', '/a/b', ['id' => 1], 'http://localhost/a/b/podcasts/1'], + 'podcasts.show-by-account-and-name' => ['podcasts/[name].blade.php', '/a/b/c', ['name' => 'Taylor'], 'http://localhost/a/b/c/podcasts/Taylor'], +])->map(function (array $value) { + $mountPath = 'resources/views/pages'; + + [$viewRelativePath, $domain, $arguments, $expectedRoute] = $value; + + return [$mountPath, $mountPath.'/'.$viewRelativePath, $domain, $arguments, $expectedRoute]; +})->mapWithKeys(fn (array $value, string $key) => [$key => [$key, $value]])->toArray()); + test('missing parameters', function (string $name, array $scenario) { [$mountPath, $viewPath, $domain, $arguments, $expectedExpectationMessage] = $scenario; From 6bc6ac3c27f59b496ca1e6e5a359c502ebfdedd6 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Wed, 16 Aug 2023 10:41:25 -0500 Subject: [PATCH 7/8] Update FolioRoutes.php --- src/FolioRoutes.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/FolioRoutes.php b/src/FolioRoutes.php index a1ef623..e66fdc5 100644 --- a/src/FolioRoutes.php +++ b/src/FolioRoutes.php @@ -17,7 +17,7 @@ class FolioRoutes { /** - * The current version of the persisted routes. + * The current version of the persisted route cache. */ protected static int $version = 1; From 6f1d08e73932ceb638cdc74be1af02eb2ce9ba89 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Wed, 16 Aug 2023 10:42:38 -0500 Subject: [PATCH 8/8] Update FolioRoutes.php --- src/FolioRoutes.php | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/FolioRoutes.php b/src/FolioRoutes.php index e66fdc5..9067c7e 100644 --- a/src/FolioRoutes.php +++ b/src/FolioRoutes.php @@ -161,8 +161,7 @@ protected function path(string $mountPath, string $path, array $parameters): arr { $uri = str_replace('.blade.php', '', $path); - $parameters = collect($parameters); - $usedParameters = collect(); + [$parameters, $usedParameters] = [collect($parameters), collect()]; $uri = collect(explode('/', $uri)) ->map(function (string $segment) use ($parameters, $uri, $usedParameters) {