Skip to content

Commit 4dfc71e

Browse files
pascalbaljetRobertBoesrojtjo
authored
Merge pull request #739 from inertiajs/custom-url-resolver
[2.x] Support for a custom URL resolver Co-authored-by: Robert Boes <[email protected]> Co-authored-by: Roj Vroemen <[email protected]>
2 parents 0b8c005 + b4b896a commit 4dfc71e

File tree

7 files changed

+103
-5
lines changed

7 files changed

+103
-5
lines changed

src/Inertia.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
* @method static void flushShared()
1414
* @method static void version(\Closure|string|null $version)
1515
* @method static string getVersion()
16+
* @method static void resolveUrlUsing(\Closure|null $urlResolver = null)
1617
* @method static \Inertia\OptionalProp optional(callable $callback)
1718
* @method static \Inertia\LazyProp lazy(callable $callback)
1819
* @method static \Inertia\DeferProp defer(callable $callback, string $group = 'default')

src/Middleware.php

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,16 @@ public function rootView(Request $request)
6969
return $this->rootView;
7070
}
7171

72+
/**
73+
* Defines a callback that returns the relative URL.
74+
*
75+
* @return Closure|null
76+
*/
77+
public function urlResolver()
78+
{
79+
return null;
80+
}
81+
7282
/**
7383
* Handle the incoming request.
7484
*
@@ -83,6 +93,10 @@ public function handle(Request $request, Closure $next)
8393
Inertia::share($this->share($request));
8494
Inertia::setRootView($this->rootView($request));
8595

96+
if ($urlResolver = $this->urlResolver()) {
97+
Inertia::resolveUrlUsing($urlResolver);
98+
}
99+
86100
$response = $next($request);
87101
$response->headers->set('Vary', Header::INERTIA);
88102

src/Response.php

Lines changed: 18 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -36,17 +36,26 @@ class Response implements Responsable
3636

3737
protected $cacheFor = [];
3838

39+
protected ?Closure $urlResolver = null;
40+
3941
/**
4042
* @param array|Arrayable $props
4143
*/
42-
public function __construct(string $component, array $props, string $rootView = 'app', string $version = '', bool $encryptHistory = false)
43-
{
44+
public function __construct(
45+
string $component,
46+
array $props,
47+
string $rootView = 'app',
48+
string $version = '',
49+
bool $encryptHistory = false,
50+
?Closure $urlResolver = null
51+
) {
4452
$this->component = $component;
4553
$this->props = $props instanceof Arrayable ? $props->toArray() : $props;
4654
$this->rootView = $rootView;
4755
$this->version = $version;
4856
$this->clearHistory = session()->pull('inertia.clear_history', false);
4957
$this->encryptHistory = $encryptHistory;
58+
$this->urlResolver = $urlResolver;
5059
}
5160

5261
/**
@@ -375,11 +384,15 @@ public function isPartial(Request $request): bool
375384
*/
376385
protected function getUrl(Request $request): string
377386
{
378-
$url = Str::start(Str::after($request->fullUrl(), $request->getSchemeAndHttpHost()), '/');
387+
$urlResolver = $this->urlResolver ?? function (Request $request) {
388+
$url = Str::start(Str::after($request->fullUrl(), $request->getSchemeAndHttpHost()), '/');
389+
390+
$rawUri = Str::before($request->getRequestUri(), '?');
379391

380-
$rawUri = Str::before($request->getRequestUri(), '?');
392+
return Str::endsWith($rawUri, '/') ? $this->finishUrlWithTrailingSlash($url) : $url;
393+
};
381394

382-
return Str::endsWith($rawUri, '/') ? $this->finishUrlWithTrailingSlash($url) : $url;
395+
return App::call($urlResolver, ['request' => $request]);
383396
}
384397

385398
/**

src/ResponseFactory.php

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,9 @@ class ResponseFactory
3131

3232
protected $encryptHistory;
3333

34+
/** @var Closure|null */
35+
protected $urlResolver;
36+
3437
/***
3538
* @param string $name The name of the root view
3639
* @return void
@@ -93,6 +96,11 @@ public function getVersion(): string
9396
return (string) $version;
9497
}
9598

99+
public function resolveUrlUsing(?Closure $urlResolver = null): void
100+
{
101+
$this->urlResolver = $urlResolver;
102+
}
103+
96104
public function clearHistory(): void
97105
{
98106
session(['inertia.clear_history' => true]);
@@ -165,6 +173,7 @@ public function render(string $component, $props = []): Response
165173
$this->rootView,
166174
$this->getVersion(),
167175
$this->encryptHistory ?? config('inertia.history.encrypt', false),
176+
$this->urlResolver,
168177
);
169178
}
170179

tests/MiddlewareTest.php

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
use Inertia\AlwaysProp;
1313
use Inertia\Inertia;
1414
use Inertia\Middleware;
15+
use Inertia\Tests\Stubs\CustomUrlResolverMiddleware;
1516
use Inertia\Tests\Stubs\ExampleMiddleware;
1617
use LogicException;
1718
use PHPUnit\Framework\Attributes\After;
@@ -130,6 +131,21 @@ public function test_it_will_instruct_inertia_to_reload_on_a_version_mismatch():
130131
self::assertEmpty($response->getContent());
131132
}
132133

134+
public function test_the_url_can_be_resolved_with_a_custom_resolver()
135+
{
136+
$this->prepareMockEndpoint(middleware: new CustomUrlResolverMiddleware);
137+
138+
$response = $this->withoutExceptionHandling()->get('/', [
139+
'X-Inertia' => 'true',
140+
]);
141+
142+
$response->assertSuccessful();
143+
$response->assertJson([
144+
'component' => 'User/Edit',
145+
'url' => '/my-custom-url',
146+
]);
147+
}
148+
133149
public function test_validation_errors_are_registered_as_of_default(): void
134150
{
135151
Route::middleware([StartSession::class, ExampleMiddleware::class])->get('/', function () {

tests/ResponseFactoryTest.php

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -127,6 +127,30 @@ public function test_the_version_can_be_a_closure(): void
127127
$response->assertJson(['component' => 'User/Edit']);
128128
}
129129

130+
public function test_the_url_can_be_resolved_with_a_custom_resolver()
131+
{
132+
Route::middleware([StartSession::class, ExampleMiddleware::class])->get('/', function () {
133+
Inertia::resolveUrlUsing(function ($request, ResponseFactory $otherDependency) {
134+
$this->assertInstanceOf(HttpRequest::class, $request);
135+
$this->assertInstanceOf(ResponseFactory::class, $otherDependency);
136+
137+
return '/my-custom-url';
138+
});
139+
140+
return Inertia::render('User/Edit');
141+
});
142+
143+
$response = $this->withoutExceptionHandling()->get('/', [
144+
'X-Inertia' => 'true',
145+
]);
146+
147+
$response->assertSuccessful();
148+
$response->assertJson([
149+
'component' => 'User/Edit',
150+
'url' => '/my-custom-url',
151+
]);
152+
}
153+
130154
public function test_shared_data_can_be_shared_from_anywhere(): void
131155
{
132156
Route::middleware([StartSession::class, ExampleMiddleware::class])->get('/', function () {
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
<?php
2+
3+
namespace Inertia\Tests\Stubs;
4+
5+
use Illuminate\Http\Request;
6+
use Illuminate\Routing\ResponseFactory;
7+
use Inertia\Middleware;
8+
use PHPUnit\Framework\Assert;
9+
10+
class CustomUrlResolverMiddleware extends Middleware
11+
{
12+
public function urlResolver()
13+
{
14+
return function ($request, ResponseFactory $otherDependency) {
15+
Assert::assertInstanceOf(Request::class, $request);
16+
Assert::assertInstanceOf(ResponseFactory::class, $otherDependency);
17+
18+
return '/my-custom-url';
19+
};
20+
}
21+
}

0 commit comments

Comments
 (0)