Skip to content

Commit ef4b9bb

Browse files
Merge pull request #75 from owenconti/default-parameters-frontend-support
feat: Adds support for providing default URL parameters via the frontend
2 parents 8f0bc01 + a719891 commit ef4b9bb

File tree

8 files changed

+94
-15
lines changed

8 files changed

+94
-15
lines changed

resources/js/wayfinder.ts

Lines changed: 29 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@ export type QueryParams = Record<
1111

1212
type Method = "get" | "post" | "put" | "delete" | "patch" | "head";
1313

14+
let urlDefaults = {};
15+
1416
export type RouteDefinition<TMethod extends Method | Method[]> = {
1517
url: string;
1618
} & (TMethod extends Method[] ? { methods: TMethod } : { method: TMethod });
@@ -23,7 +25,7 @@ export type RouteFormDefinition<TMethod extends Method> = {
2325
export type RouteQueryOptions = {
2426
query?: QueryParams;
2527
mergeQuery?: QueryParams;
26-
}
28+
};
2729

2830
export const queryParams = (options?: RouteQueryOptions) => {
2931
if (!options || (!options.query && !options.mergeQuery)) {
@@ -91,6 +93,32 @@ export const queryParams = (options?: RouteQueryOptions) => {
9193
return str.length > 0 ? `?${str}` : "";
9294
};
9395

96+
export const setUrlDefaults = (params: Record<string, unknown>) => {
97+
urlDefaults = params;
98+
};
99+
100+
export const addUrlDefault = (
101+
key: string,
102+
value: string | number | boolean,
103+
) => {
104+
urlDefaults[key] = value;
105+
};
106+
107+
export const applyUrlDefaults = <T>(existing: T): T => {
108+
const existingParams = { ...existing };
109+
110+
for (const key in urlDefaults) {
111+
if (
112+
existingParams[key] === undefined &&
113+
urlDefaults[key] !== undefined
114+
) {
115+
existingParams[key] = urlDefaults[key];
116+
}
117+
}
118+
119+
return existingParams;
120+
};
121+
94122
export const validateParameters = (
95123
args: Record<string, unknown> | undefined,
96124
optional: string[],

resources/method.blade.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,8 @@
3131
@endforeach
3232
}
3333
}
34+
35+
args = applyUrlDefaults(args)
3436
@endif
3537

3638
@if ($parameters->where('optional')->isNotEmpty())

src/GenerateCommand.php

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
use Illuminate\Routing\Router;
99
use Illuminate\Routing\UrlGenerator;
1010
use Illuminate\Support\Collection;
11+
use Illuminate\Support\Facades\URL;
1112
use Illuminate\Support\Str;
1213
use Illuminate\View\Factory;
1314
use ReflectionProperty;
@@ -54,7 +55,9 @@ public function handle()
5455
$this->forcedScheme = (new ReflectionProperty($this->url, 'forceScheme'))->getValue($this->url);
5556
$this->forcedRoot = (new ReflectionProperty($this->url, 'forcedRoot'))->getValue($this->url);
5657

57-
$routes = collect($this->router->getRoutes())->map(function (BaseRoute $route) {
58+
$globalUrlDefaults = collect(URL::getDefaultParameters())->map(fn ($v) => is_scalar($v) || is_null($v) ? $v : '');
59+
60+
$routes = collect($this->router->getRoutes())->map(function (BaseRoute $route) use ($globalUrlDefaults) {
5861
$defaults = collect($this->router->gatherRouteMiddleware($route))->map(function ($middleware) {
5962
if ($middleware instanceof \Closure) {
6063
return [];
@@ -65,7 +68,7 @@ public function handle()
6568
return $this->urlDefaults[$middleware];
6669
})->flatMap(fn ($r) => $r);
6770

68-
return new Route($route, $defaults, $this->forcedScheme, $this->forcedRoot);
71+
return new Route($route, $globalUrlDefaults->merge($defaults), $this->forcedScheme, $this->forcedRoot);
6972
});
7073

7174
if (! $this->option('skip-actions')) {
@@ -228,6 +231,10 @@ private function appendCommonImports(Collection $routes, string $path, string $n
228231
$imports[] = 'type RouteFormDefinition';
229232
}
230233

234+
if ($routes->contains(fn (Route $route) => $route->parameters()->isNotEmpty())) {
235+
$imports[] = 'applyUrlDefaults';
236+
}
237+
231238
if ($routes->contains(fn (Route $route) => $route->parameters()->contains(fn (Parameter $parameter) => $parameter->optional))) {
232239
$imports[] = 'validateParameters';
233240
}
@@ -340,6 +347,12 @@ private function getDefaultsForMiddleware(string $middleware)
340347
}
341348

342349
$methodContents = str($methodContents)->after('{')->beforeLast('}')->trim();
350+
351+
return $this->extractUrlDefaults($methodContents);
352+
}
353+
354+
private function extractUrlDefaults(string $methodContents): array
355+
{
343356
$tokens = token_get_all('<?php '.$methodContents);
344357
$foundUrlFacade = false;
345358
$defaults = [];

tests/DefaultParameters.test.ts

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
import { expect, test } from "vitest";
2+
import {
3+
defaultParametersDomain,
4+
fixedDomain,
5+
} from "../workbench/resources/js/actions/App/Http/Controllers/DomainController";
6+
import { setUrlDefaults } from "../workbench/resources/js/wayfinder";
7+
8+
test("it can generate urls without default parameters set", () => {
9+
expect(fixedDomain.url({ param: "foo" })).toBe(
10+
"//example.test/fixed-domain/foo",
11+
);
12+
});
13+
14+
test("it can generate urls with default URL parameters set on backend and frontend", () => {
15+
setUrlDefaults({
16+
defaultDomain: "tim.macdonald",
17+
});
18+
19+
expect(
20+
defaultParametersDomain.url({
21+
param: "foo",
22+
}),
23+
).toBe("//tim.macdonald.au/default-parameters-domain/foo");
24+
});

tests/DomainController.test.ts

Lines changed: 17 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,20 @@
11
import { expect, test } from "vitest";
2-
import { dynamicDomain, fixedDomain } from "../workbench/resources/js/actions/App/Http/Controllers/DomainController";
2+
import {
3+
defaultParametersDomain,
4+
fixedDomain,
5+
} from "../workbench/resources/js/actions/App/Http/Controllers/DomainController";
36

4-
test('can generate fixed domain urls', () => {
5-
expect(fixedDomain.url({ param: 'foo' })).toBe('//example.test/fixed-domain/foo')
6-
})
7+
test("can generate fixed domain urls", () => {
8+
expect(fixedDomain.url({ param: "foo" })).toBe(
9+
"//example.test/fixed-domain/foo",
10+
);
11+
});
712

8-
test('can generate dynamic domain urls', () => {
9-
expect(dynamicDomain.url({
10-
domain: 'tim.macdonald',
11-
param: 'foo',
12-
})).toBe('//tim.macdonald.au/dynamic-domain/foo')
13-
})
13+
test("can generate dynamic domain urls", () => {
14+
expect(
15+
defaultParametersDomain.url({
16+
defaultDomain: "tim.macdonald",
17+
param: "foo",
18+
}),
19+
).toBe("//tim.macdonald.au/default-parameters-domain/foo");
20+
});

workbench/app/Http/Controllers/DomainController.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ public function fixedDomain()
99
//
1010
}
1111

12-
public function dynamicDomain()
12+
public function defaultParametersDomain()
1313
{
1414
//
1515
}

workbench/app/Providers/WorkbenchServiceProvider.php

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
namespace App\Providers;
44

55
use Illuminate\Support\Facades\Config;
6+
use Illuminate\Support\Facades\URL;
67
use Illuminate\Support\ServiceProvider;
78

89
class WorkbenchServiceProvider extends ServiceProvider
@@ -20,6 +21,10 @@ public function register(): void
2021
'throw' => false,
2122
],
2223
]);
24+
25+
URL::defaults([
26+
'defaultDomain' => 'tim.macdonald',
27+
]);
2328
}
2429

2530
/**

workbench/routes/web.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,7 @@
5858
Route::get('/parameter-names/{SCREAMING_SNAKE_CASE}/screaming-snake', [ParameterNameController::class, 'screamingSnake']);
5959

6060
Route::domain('example.test')->get('/fixed-domain/{param}', [DomainController::class, 'fixedDomain']);
61-
Route::domain('{domain}.au')->get('/dynamic-domain/{param}', [DomainController::class, 'dynamicDomain']);
61+
Route::domain('{defaultDomain}.au')->get('/default-parameters-domain/{param}', [DomainController::class, 'defaultParametersDomain']);
6262

6363
Route::get('/nested/controller', [NestedController::class, 'nested']);
6464

0 commit comments

Comments
 (0)