From ff59a30c5345dc29964c220e1dcd15440ad0759f Mon Sep 17 00:00:00 2001 From: Rasmus Bertell <57398362+Rasmus-Bertell@users.noreply.github.com> Date: Sat, 1 Jul 2023 01:14:59 +0300 Subject: [PATCH] camelCase route parameter so multi-word model binding works (#670) --- src/Extracting/Shared/UrlParamsNormalizer.php | 3 +++ tests/Fixtures/TestController.php | 5 ++++ tests/Unit/ExtractedEndpointDataTest.php | 27 ++++++++++++++++--- 3 files changed, 31 insertions(+), 4 deletions(-) diff --git a/src/Extracting/Shared/UrlParamsNormalizer.php b/src/Extracting/Shared/UrlParamsNormalizer.php index 9607cb28..bfb21b0d 100644 --- a/src/Extracting/Shared/UrlParamsNormalizer.php +++ b/src/Extracting/Shared/UrlParamsNormalizer.php @@ -185,6 +185,9 @@ protected static function getInlineRouteKey(Route $route, string $paramName): ?s */ protected static function getRouteKeyFromModel(string $paramName, array $typeHintedEloquentModels): ?string { + // Ensure param name is in camelCase so it matches the argument name (e.g. The '$userAddress' in `function show(BigThing $userAddress`) + $paramName = Str::camel($paramName); + if (array_key_exists($paramName, $typeHintedEloquentModels)) { $argumentInstance = $typeHintedEloquentModels[$paramName]; return $argumentInstance->getRouteKeyName(); diff --git a/tests/Fixtures/TestController.php b/tests/Fixtures/TestController.php index 2b8cfd0f..43b0992e 100644 --- a/tests/Fixtures/TestController.php +++ b/tests/Fixtures/TestController.php @@ -585,6 +585,11 @@ public function withInjectedModel(TestUser $user) { return null; } + + public function withInjectedModelFullParamName(TestPost $testPost) + { + return null; + } public function withEnumRule(Request $request) { diff --git a/tests/Unit/ExtractedEndpointDataTest.php b/tests/Unit/ExtractedEndpointDataTest.php index b61a3fed..5a6aed96 100644 --- a/tests/Unit/ExtractedEndpointDataTest.php +++ b/tests/Unit/ExtractedEndpointDataTest.php @@ -9,6 +9,7 @@ use Knuckles\Scribe\Scribe; use Knuckles\Scribe\Tests\BaseLaravelTest; use Knuckles\Scribe\Tests\Fixtures\TestController; +use Knuckles\Scribe\Tools\Utils as u; class ExtractedEndpointDataTest extends BaseLaravelTest { @@ -99,10 +100,20 @@ public function normalizes_nonresource_url_params_with_inline_bindings() $this->assertEquals('things/{thing}', $this->originalUri($route)); $this->assertEquals('things/{thing_slug}', $this->expectedUri($route)); } + + /** @test */ + public function normalizes_url_param_with_eloquent_model_binding() + { + Route::get("test-posts/{test_post}", [TestController::class, 'withInjectedModelFullParamName']); + $route = $this->getRoute(['prefixes' => '*']); + + $this->assertEquals('test-posts/{test_post}', $this->originalUri($route)); + $this->assertEquals('test-posts/{test_post_slug}', $this->expectedUri($route, withReflectedMethod: true)); + } - protected function expectedUri(LaravelRoute $route): string + protected function expectedUri(LaravelRoute $route, $withReflectedMethod = false): string { - return $this->endpoint($route)->uri; + return $this->endpoint($route, $withReflectedMethod)->uri; } protected function originalUri(LaravelRoute $route): string @@ -110,13 +121,21 @@ protected function originalUri(LaravelRoute $route): string return $route->uri; } - protected function endpoint(LaravelRoute $route): ExtractedEndpointData + protected function endpoint(LaravelRoute $route, $withReflectedMethod = false): ExtractedEndpointData { + if ($withReflectedMethod) { + [$controllerName, $methodName] = u::getRouteClassAndMethodNames($route); + $method = u::getReflectedRouteMethod([$controllerName, $methodName]); + } else { + // We're testing resource routes, and we may not have methods that exist for all of them (show, index, etc). + // Just use this dummy so we don't have null + $method = new \ReflectionFunction('dump'); + } return new ExtractedEndpointData([ 'route' => $route, 'uri' => $route->uri, 'httpMethods' => $route->methods, - 'method' => new \ReflectionFunction('dump'), // Just so we don't have null + 'method' => $method, ]); }