Jelajahi Sumber

Allow users customize endpoint URL normalization

shalvah 2 tahun lalu
induk
melakukan
fe70df9ef0

+ 6 - 1
camel/Extraction/ExtractedEndpointData.php

@@ -5,6 +5,7 @@ namespace Knuckles\Camel\Extraction;
 use Illuminate\Routing\Route;
 use Knuckles\Camel\BaseDTO;
 use Knuckles\Scribe\Extracting\Shared\UrlParamsNormalizer;
+use Knuckles\Scribe\Tools\Globals;
 use Knuckles\Scribe\Tools\Utils as u;
 use ReflectionClass;
 
@@ -86,7 +87,11 @@ class ExtractedEndpointData extends BaseDTO
 
         parent::__construct($parameters);
 
-        $this->uri = UrlParamsNormalizer::normalizeParameterNamesInRouteUri($this->route, $this->method);
+        $this->uri = match (is_callable(Globals::$__normalizeEndpointUrlUsing)) {
+            true => call_user_func_array(Globals::$__normalizeEndpointUrlUsing,
+                [$this->route->uri, $this->route, $this->method, $this->controller]),
+            default => UrlParamsNormalizer::normalizeParameterNamesInRouteUri($this->route, $this->method),
+        };
     }
 
     public static function fromRoute(Route $route, array $extras = []): self

+ 16 - 2
src/Scribe.php

@@ -52,10 +52,24 @@ class Scribe
      * to instantiate Form Requests. his callback takes the name of the form request class,
      * the current Laravel route being processed, and the controller method.
      *
-     * @param callable(string,\Illuminate\Routing\Route,\ReflectionFunctionAbstract): mixed $callable
+     * @param ?callable(string,\Illuminate\Routing\Route,\ReflectionFunctionAbstract): mixed $callable
      */
-    public static function instantiateFormRequestUsing(callable $callable)
+    public static function instantiateFormRequestUsing(?callable $callable)
     {
         Globals::$__instantiateFormRequestUsing = $callable;
     }
+
+    /**
+     * Specify a callback that will be called when instantiating an `ExtractedEndpointData` object
+     * in order to normalize the URL. The default normalization tries to convert URL parameters from
+     * Laravel resource-style (`users/{user}/projects/{project}`)
+     * to a general style (`users/{user_id}/projects/{id}`).
+     * The callback will be passed the existing URL, the route object, the controller method and class.
+     *
+     * @param ?callable(string,\Illuminate\Routing\Route,\ReflectionFunctionAbstract,?\ReflectionClass): string $callable
+     */
+    public static function normalizeEndpointUrlUsing(?callable $callable)
+    {
+        Globals::$__normalizeEndpointUrlUsing = $callable;
+    }
 }

+ 2 - 0
src/Tools/Globals.php

@@ -15,4 +15,6 @@ class Globals
     public static $__afterGenerating;
 
     public static $__instantiateFormRequestUsing;
+
+    public static $__normalizeEndpointUrlUsing;
 }

+ 23 - 0
tests/Unit/ExtractedEndpointDataTest.php

@@ -6,6 +6,7 @@ use Illuminate\Routing\Route as LaravelRoute;
 use Illuminate\Support\Facades\Route;
 use Knuckles\Camel\Extraction\ExtractedEndpointData;
 use Knuckles\Scribe\Matching\RouteMatcher;
+use Knuckles\Scribe\Scribe;
 use Knuckles\Scribe\Tests\BaseLaravelTest;
 use Knuckles\Scribe\Tests\Fixtures\TestController;
 
@@ -28,6 +29,28 @@ class ExtractedEndpointDataTest extends BaseLaravelTest
         $this->assertEquals('things/{thing_id}/otherthings/{id}', $this->expectedUri($route));
     }
 
+    /** @test */
+    public function allows_user_specified_normalization()
+    {
+        Scribe::normalizeEndpointUrlUsing(function (string $url, LaravelRoute $route, \ReflectionFunctionAbstract $method, ?\ReflectionClass $controller) {
+            if ($url == 'things/{thing}') return 'things/{the_id_of_the_thing}';
+
+            if ($route->named('things.otherthings.destroy')) return 'things/{thing-id}/otherthings/{other_thing-id}';
+        });
+
+        Route::apiResource('things', TestController::class)->only('show');
+        $route = $this->getRoute(['prefixes' => '*']);
+        $this->assertEquals('things/{thing}', $this->originalUri($route));
+        $this->assertEquals('things/{the_id_of_the_thing}', $this->expectedUri($route));
+
+        Route::apiResource('things.otherthings', TestController::class)->only('destroy');
+        $route = $this->getRoute(['prefixes' => '*/otherthings/*']);
+        $this->assertEquals('things/{thing}/otherthings/{otherthing}', $this->originalUri($route));
+        $this->assertEquals('things/{thing-id}/otherthings/{other_thing-id}', $this->expectedUri($route));
+
+        Scribe::normalizeEndpointUrlUsing(null);
+    }
+
     /** @test */
     public function normalizes_resource_url_params_from_underscores_to_hyphens()
     {