소스 검색

camelCase route parameter so multi-word model binding works (#670)

Rasmus Bertell 1 년 전
부모
커밋
ff59a30c53
3개의 변경된 파일31개의 추가작업 그리고 4개의 파일을 삭제
  1. 3 0
      src/Extracting/Shared/UrlParamsNormalizer.php
  2. 5 0
      tests/Fixtures/TestController.php
  3. 23 4
      tests/Unit/ExtractedEndpointDataTest.php

+ 3 - 0
src/Extracting/Shared/UrlParamsNormalizer.php

@@ -185,6 +185,9 @@ class UrlParamsNormalizer
      */
     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();

+ 5 - 0
tests/Fixtures/TestController.php

@@ -585,6 +585,11 @@ class TestController extends Controller
     {
         return null;
     }
+    
+    public function withInjectedModelFullParamName(TestPost $testPost)
+    {
+        return null;
+    }
 
     public function withEnumRule(Request $request)
     {

+ 23 - 4
tests/Unit/ExtractedEndpointDataTest.php

@@ -9,6 +9,7 @@ use Knuckles\Scribe\Matching\RouteMatcher;
 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 @@ class ExtractedEndpointDataTest extends BaseLaravelTest
         $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 @@ class ExtractedEndpointDataTest extends BaseLaravelTest
         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,
         ]);
     }