Skip to content

Commit bfae861

Browse files
Introduce InitialProp class
1 parent 04d84a3 commit bfae861

11 files changed

+137
-87
lines changed

src/FirstLoad.php

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
<?php
2+
3+
namespace Inertia;
4+
5+
interface FirstLoad
6+
{
7+
//
8+
}

src/Inertia.php

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,6 @@
1414
* @method static void version(\Closure|string|null $version)
1515
* @method static string getVersion()
1616
* @method static void resolveUrlUsing(\Closure|null $urlResolver = null)
17-
* @method static void resolveOncePropsUsing(\Closure|null $urlResolver = null)
1817
* @method static \Inertia\OptionalProp optional(callable $callback)
1918
* @method static \Inertia\LazyProp lazy(callable $callback)
2019
* @method static \Inertia\DeferProp defer(callable $callback, string $group = 'default')

src/InitialProp.php

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
<?php
2+
3+
namespace Inertia;
4+
5+
use Illuminate\Support\Facades\App;
6+
7+
class InitialProp implements FirstLoad
8+
{
9+
protected $callback;
10+
11+
public function __construct(callable $callback)
12+
{
13+
$this->callback = $callback;
14+
}
15+
16+
public function __invoke()
17+
{
18+
return App::call($this->callback);
19+
}
20+
}

src/Middleware.php

Lines changed: 0 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -79,16 +79,6 @@ public function urlResolver()
7979
return null;
8080
}
8181

82-
/**
83-
* Defines the props that are shared for one time.
84-
*
85-
* @return Closure|null
86-
*/
87-
public function oncePropsResolver()
88-
{
89-
return null;
90-
}
91-
9282
/**
9383
* Handle the incoming request.
9484
*
@@ -107,10 +97,6 @@ public function handle(Request $request, Closure $next)
10797
Inertia::resolveUrlUsing($urlResolver);
10898
}
10999

110-
if ($oncePropsResolver = $this->oncePropsResolver()) {
111-
Inertia::resolveOncePropsUsing($oncePropsResolver);
112-
}
113-
114100
$response = $next($request);
115101
$response->headers->set('Vary', Header::INERTIA);
116102

src/Response.php

Lines changed: 19 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -38,8 +38,6 @@ class Response implements Responsable
3838

3939
protected ?Closure $urlResolver = null;
4040

41-
protected ?Closure $oncePropsResolver = null;
42-
4341
/**
4442
* @param array|Arrayable $props
4543
*/
@@ -50,7 +48,6 @@ public function __construct(
5048
string $version = '',
5149
bool $encryptHistory = false,
5250
?Closure $urlResolver = null,
53-
?Closure $oncePropsResolver = null,
5451
) {
5552
$this->component = $component;
5653
$this->props = $props instanceof Arrayable ? $props->toArray() : $props;
@@ -59,7 +56,6 @@ public function __construct(
5956
$this->clearHistory = session()->pull('inertia.clear_history', false);
6057
$this->encryptHistory = $encryptHistory;
6158
$this->urlResolver = $urlResolver;
62-
$this->oncePropsResolver = $oncePropsResolver;
6359
}
6460

6561
/**
@@ -130,14 +126,13 @@ public function toResponse($request)
130126
$this->resolveMergeProps($request),
131127
$this->resolveDeferredProps($request),
132128
$this->resolveCacheDirections($request),
129+
$this->resolveInitialProps($request),
133130
);
134131

135132
if ($request->header(Header::INERTIA)) {
136133
return new JsonResponse($page, 200, [Header::INERTIA => 'true']);
137134
}
138135

139-
$page += $this->resolveOnceProps($request);
140-
141136
return ResponseFactory::view($this->rootView, $this->viewData + ['page' => $page]);
142137
}
143138

@@ -150,6 +145,7 @@ public function resolveProperties(Request $request, array $props): array
150145
$props = $this->resolveArrayableProperties($props, $request);
151146
$props = $this->resolveAlways($props);
152147
$props = $this->resolvePropertyInstances($props, $request);
148+
$props = $this->removeInitialProperties($props);
153149

154150
return $props;
155151
}
@@ -302,6 +298,14 @@ public function resolvePropertyInstances(array $props, Request $request): array
302298
return $props;
303299
}
304300

301+
/**
302+
* Remove initial properties from the response.
303+
*/
304+
public function removeInitialProperties(array $props): array
305+
{
306+
return array_filter($props, static fn ($prop) => ! $prop instanceof InitialProp);
307+
}
308+
305309
/**
306310
* Resolve the cache directions for the response.
307311
*/
@@ -382,13 +386,17 @@ public function resolveDeferredProps(Request $request): array
382386
return $deferredProps->isNotEmpty() ? ['deferredProps' => $deferredProps->toArray()] : [];
383387
}
384388

385-
public function resolveOnceProps(Request $request): array
389+
public function resolveInitialProps(Request $request): array
386390
{
387-
$onceProps = $this->oncePropsResolver
388-
? App::call($this->oncePropsResolver, ['request' => $request])
389-
: [];
391+
if ($request->header(Header::INERTIA)) {
392+
return [];
393+
}
394+
395+
$initialProps = collect($this->props)
396+
->filter(fn ($prop) => $prop instanceof InitialProp)
397+
->mapWithKeys(fn ($value, $key) => [$key => App::call($value)]);
390398

391-
return empty($onceProps) ? [] : ['onceProps' => $onceProps];
399+
return $initialProps->isNotEmpty() ? ['initialProps' => $initialProps->toArray()] : [];
392400
}
393401

394402
/**

src/ResponseFactory.php

Lines changed: 6 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -35,12 +35,8 @@ class ResponseFactory
3535
/** @var Closure|null */
3636
protected $urlResolver;
3737

38-
/** @var Closure|null */
39-
protected $oncePropsResolver;
40-
4138
/**
42-
* @param string $name The name of the root view
43-
* @return void
39+
* @param string $name The name of the root view
4440
*/
4541
public function setRootView(string $name): void
4642
{
@@ -105,11 +101,6 @@ public function resolveUrlUsing(?Closure $urlResolver = null): void
105101
$this->urlResolver = $urlResolver;
106102
}
107103

108-
public function resolveOncePropsUsing(?Closure $oncePropsResolver = null): void
109-
{
110-
$this->oncePropsResolver = $oncePropsResolver;
111-
}
112-
113104
public function clearHistory(): void
114105
{
115106
session(['inertia.clear_history' => true]);
@@ -136,6 +127,11 @@ public function optional(callable $callback): OptionalProp
136127
return new OptionalProp($callback);
137128
}
138129

130+
public function initial(callable $callback): InitialProp
131+
{
132+
return new InitialProp($callback);
133+
}
134+
139135
public function defer(callable $callback, string $group = 'default'): DeferProp
140136
{
141137
return new DeferProp($callback, $group);
@@ -197,7 +193,6 @@ public function render(string $component, $props = []): Response
197193
$this->getVersion(),
198194
$this->encryptHistory ?? config('inertia.history.encrypt', false),
199195
$this->urlResolver,
200-
$this->oncePropsResolver,
201196
);
202197
}
203198

stubs/middleware.stub

Lines changed: 0 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -40,16 +40,4 @@ class {{ class }} extends Middleware
4040
//
4141
];
4242
}
43-
44-
/**
45-
* Defines the props that are shared for one time.
46-
*
47-
* @return Closure
48-
*/
49-
public function oncePropsResolver()
50-
{
51-
return function (Request $request): array {
52-
return [];
53-
};
54-
}
5543
}

tests/InitialPropTest.php

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
<?php
2+
3+
namespace Inertia\Tests;
4+
5+
use Illuminate\Http\Request;
6+
use Illuminate\Session\Middleware\StartSession;
7+
use Illuminate\Support\Facades\Route;
8+
use Inertia\Inertia;
9+
use Inertia\InitialProp;
10+
use Inertia\Tests\Stubs\ExampleMiddleware;
11+
12+
class InitialPropTest extends TestCase
13+
{
14+
public function test_initial_props_accessibility()
15+
{
16+
Inertia::share([
17+
'initial' => Inertia::initial(fn () => true),
18+
'appName' => Inertia::initial(fn () => 'test'),
19+
]);
20+
21+
$this->prepareMockEndpoint();
22+
23+
$response = $this->withoutExceptionHandling()->get('/');
24+
25+
$response->assertSuccessful();
26+
$this->assertSame(
27+
'<div id="app" data-page="{&quot;component&quot;:&quot;User\/Edit&quot;,&quot;props&quot;:{&quot;errors&quot;:{}},&quot;url&quot;:&quot;\/&quot;,&quot;version&quot;:&quot;&quot;,&quot;clearHistory&quot;:false,&quot;encryptHistory&quot;:false,&quot;initialProps&quot;:{&quot;initial&quot;:true,&quot;appName&quot;:&quot;test&quot;}}"></div>',
28+
$response->content(),
29+
);
30+
}
31+
32+
public function test_initial_props_are_not_accessible()
33+
{
34+
Inertia::share([
35+
'initial' => Inertia::initial(fn () => true),
36+
'appName' => Inertia::initial(fn () => 'test'),
37+
]);
38+
39+
$this->prepareMockEndpoint();
40+
41+
$response = $this->withoutExceptionHandling()->get('/', [
42+
'X-Inertia' => 'true',
43+
]);
44+
45+
$response->assertSuccessful();
46+
$response->assertJsonMissingPath('initialProps');
47+
}
48+
49+
public function test_can_invoke(): void
50+
{
51+
$initialProp = new InitialProp(function () {
52+
return 'A initial value';
53+
});
54+
55+
$this->assertSame('A initial value', $initialProp());
56+
}
57+
58+
public function test_can_resolve_bindings_when_invoked(): void
59+
{
60+
$initialProp = new InitialProp(function (Request $request) {
61+
return $request;
62+
});
63+
64+
$this->assertInstanceOf(Request::class, $initialProp());
65+
}
66+
67+
private function prepareMockEndpoint(): \Illuminate\Routing\Route
68+
{
69+
return Route::middleware([StartSession::class, ExampleMiddleware::class])->get('/', function () {
70+
return Inertia::render('User/Edit');
71+
});
72+
}
73+
}

tests/MiddlewareTest.php

Lines changed: 0 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,6 @@
1212
use Inertia\AlwaysProp;
1313
use Inertia\Inertia;
1414
use Inertia\Middleware;
15-
use Inertia\Tests\Stubs\CustomOncePropsResolverMiddleware;
1615
use Inertia\Tests\Stubs\CustomUrlResolverMiddleware;
1716
use Inertia\Tests\Stubs\ExampleMiddleware;
1817
use LogicException;
@@ -147,19 +146,6 @@ public function test_the_url_can_be_resolved_with_a_custom_resolver()
147146
]);
148147
}
149148

150-
public function test_the_once_props_can_be_resolved_with_a_custom_resolver()
151-
{
152-
$this->prepareMockEndpoint(middleware: new CustomOncePropsResolverMiddleware);
153-
154-
$response = $this->withoutExceptionHandling()->get('/');
155-
156-
$response->assertSuccessful();
157-
$this->assertSame(
158-
'<div id="app" data-page="{&quot;component&quot;:&quot;User\/Edit&quot;,&quot;props&quot;:{&quot;errors&quot;:{},&quot;user&quot;:{&quot;name&quot;:&quot;Jonathan&quot;}},&quot;url&quot;:&quot;\/&quot;,&quot;version&quot;:&quot;&quot;,&quot;clearHistory&quot;:false,&quot;encryptHistory&quot;:false,&quot;onceProps&quot;:{&quot;once&quot;:true,&quot;appName&quot;:&quot;test&quot;}}"></div>',
159-
$response->content(),
160-
);
161-
}
162-
163149
public function test_validation_errors_are_registered_as_of_default(): void
164150
{
165151
Route::middleware([StartSession::class, ExampleMiddleware::class])->get('/', function () {

tests/ResponseFactoryTest.php

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
use Inertia\ComponentNotFoundException;
1616
use Inertia\DeferProp;
1717
use Inertia\Inertia;
18+
use Inertia\InitialProp;
1819
use Inertia\LazyProp;
1920
use Inertia\MergeProp;
2021
use Inertia\OptionalProp;
@@ -342,6 +343,16 @@ public function test_can_create_optional_prop(): void
342343
$this->assertInstanceOf(OptionalProp::class, $optionalProp);
343344
}
344345

346+
public function test_can_create_initial_prop(): void
347+
{
348+
$factory = new ResponseFactory;
349+
$initialProp = $factory->initial(function () {
350+
return 'An initial value';
351+
});
352+
353+
$this->assertInstanceOf(InitialProp::class, $initialProp);
354+
}
355+
345356
public function test_can_create_always_prop(): void
346357
{
347358
$factory = new ResponseFactory;

0 commit comments

Comments
 (0)