Skip to content

Commit 0f4bdcf

Browse files
committed
Adds ProvidesInertiaProps interface and support in responses
1 parent 96f9766 commit 0f4bdcf

File tree

6 files changed

+140
-4
lines changed

6 files changed

+140
-4
lines changed

src/ProvidesInertiaProps.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 ProvidesInertiaProps
6+
{
7+
public function toInertiaProps(RenderContext $context): iterable;
8+
}

src/RenderContext.php

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
<?php
2+
3+
namespace Inertia;
4+
5+
use Illuminate\Http\Request;
6+
7+
class RenderContext
8+
{
9+
public function __construct(
10+
public string $component,
11+
public Request $request
12+
) {
13+
//
14+
}
15+
}

src/Response.php

Lines changed: 30 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -50,13 +50,15 @@ public function __construct(string $component, array $props, string $rootView =
5050
}
5151

5252
/**
53-
* @param string|array $key
53+
* @param string|array|ProvidesInertiaProps $key
5454
* @param mixed $value
5555
* @return $this
5656
*/
5757
public function with($key, $value = null): self
5858
{
59-
if (is_array($key)) {
59+
if ($key instanceof ProvidesInertiaProps) {
60+
$this->props[] = $key;
61+
} elseif (is_array($key)) {
6062
$this->props = array_merge($this->props, $key);
6163
} else {
6264
$this->props[$key] = $value;
@@ -131,6 +133,7 @@ public function toResponse($request)
131133
*/
132134
public function resolveProperties(Request $request, array $props): array
133135
{
136+
$props = $this->resolveInertiaPropsProviders($props, $request);
134137
$props = $this->resolvePartialProperties($props, $request);
135138
$props = $this->resolveArrayableProperties($props, $request);
136139
$props = $this->resolveAlways($props);
@@ -139,13 +142,37 @@ public function resolveProperties(Request $request, array $props): array
139142
return $props;
140143
}
141144

145+
/**
146+
* Resolve the ProvidesInertiaProps props.
147+
*/
148+
public function resolveInertiaPropsProviders(array $props, Request $request): array
149+
{
150+
$newProps = [];
151+
152+
$renderContext = new RenderContext($this->component, $request);
153+
154+
foreach ($props as $key => $value) {
155+
if (is_numeric($key) && $value instanceof ProvidesInertiaProps) {
156+
// Pipe into a Collection to leverage Collection::getArrayableItems()
157+
$newProps = array_merge(
158+
$newProps,
159+
collect($value->toInertiaProps($renderContext))->all()
160+
);
161+
} else {
162+
$newProps[$key] = $value;
163+
}
164+
}
165+
166+
return $newProps;
167+
}
168+
142169
/**
143170
* Resolve the `only` and `except` partial request props.
144171
*/
145172
public function resolvePartialProperties(array $props, Request $request): array
146173
{
147174
if (! $this->isPartial($request)) {
148-
return array_filter($this->props, static function ($prop) {
175+
return array_filter($props, static function ($prop) {
149176
return ! ($prop instanceof IgnoreFirstLoad);
150177
});
151178
}

src/ResponseFactory.php

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -151,14 +151,19 @@ public function always($value): AlwaysProp
151151
}
152152

153153
/**
154-
* @param array|Arrayable $props
154+
* @param array|Arrayable|ProvidesInertiaProps $props
155155
*/
156156
public function render(string $component, $props = []): Response
157157
{
158158
if ($props instanceof Arrayable) {
159159
$props = $props->toArray();
160160
}
161161

162+
if ($props instanceof ProvidesInertiaProps) {
163+
// Will be resolved in Response::resolveResponsableProperties()
164+
$props = [$props];
165+
}
166+
162167
return new Response(
163168
$component,
164169
array_merge($this->sharedProps, $props),

tests/ResponseFactoryTest.php

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@
1717
use Inertia\LazyProp;
1818
use Inertia\MergeProp;
1919
use Inertia\OptionalProp;
20+
use Inertia\ProvidesInertiaProps;
21+
use Inertia\RenderContext;
2022
use Inertia\ResponseFactory;
2123
use Inertia\Tests\Stubs\ExampleMiddleware;
2224

@@ -352,4 +354,30 @@ public function toArray()
352354
],
353355
]);
354356
}
357+
358+
public function test_will_accept_instances_of_provides_inertia_props()
359+
{
360+
Route::middleware([StartSession::class, ExampleMiddleware::class])->get('/', function () {
361+
Inertia::share('foo', 'bar');
362+
363+
return Inertia::render('User/Edit', new class implements ProvidesInertiaProps
364+
{
365+
public function toInertiaProps(RenderContext $context): iterable
366+
{
367+
return [
368+
'foo' => 'bar',
369+
];
370+
}
371+
});
372+
});
373+
374+
$response = $this->withoutExceptionHandling()->get('/', ['X-Inertia' => 'true']);
375+
$response->assertSuccessful();
376+
$response->assertJson([
377+
'component' => 'User/Edit',
378+
'props' => [
379+
'foo' => 'bar',
380+
],
381+
]);
382+
}
355383
}

tests/ResponseTest.php

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@
1717
use Inertia\Inertia;
1818
use Inertia\LazyProp;
1919
use Inertia\MergeProp;
20+
use Inertia\ProvidesInertiaProps;
21+
use Inertia\RenderContext;
2022
use Inertia\Response;
2123
use Inertia\Tests\Stubs\FakeResource;
2224
use Inertia\Tests\Stubs\MergeWithSharedProp;
@@ -759,6 +761,33 @@ public function test_always_props_are_included_on_partial_reload(): void
759761
$this->assertFalse(isset($page->props->user));
760762
}
761763

764+
public function test_inertia_responsable_objects(): void
765+
{
766+
$request = Request::create('/user/123', 'GET');
767+
768+
$response = new Response('User/Edit', [
769+
'foo' => 'bar',
770+
new class implements ProvidesInertiaProps
771+
{
772+
public function toInertiaProps(RenderContext $context): iterable
773+
{
774+
return collect([
775+
'baz' => 'qux',
776+
]);
777+
}
778+
},
779+
'quux' => 'corge',
780+
781+
], 'app', '123');
782+
$response = $response->toResponse($request);
783+
$view = $response->getOriginalContent();
784+
$page = $view->getData()['page'];
785+
786+
$this->assertSame('bar', $page['props']['foo']);
787+
$this->assertSame('qux', $page['props']['baz']);
788+
$this->assertSame('corge', $page['props']['quux']);
789+
}
790+
762791
public function test_inertia_response_type_prop(): void
763792
{
764793
$request = Request::create('/user/123', 'GET');
@@ -836,6 +865,30 @@ public function test_nested_dot_props_do_not_get_unpacked(): void
836865
$this->assertFalse(array_key_exists('can', $auth));
837866
}
838867

868+
public function test_props_can_be_added_using_the_with_method(): void
869+
{
870+
$request = Request::create('/user/123', 'GET');
871+
$response = new Response('User/Edit', [], 'app', '123');
872+
873+
$response->with(['foo' => 'bar', 'baz' => 'qux'])
874+
->with(['quux' => 'corge'])
875+
->with(new class implements ProvidesInertiaProps
876+
{
877+
public function toInertiaProps(RenderContext $context): iterable
878+
{
879+
return collect(['grault' => 'garply']);
880+
}
881+
});
882+
883+
$response = $response->toResponse($request);
884+
$view = $response->getOriginalContent();
885+
$page = $view->getData()['page'];
886+
887+
$this->assertSame('bar', $page['props']['foo']);
888+
$this->assertSame('qux', $page['props']['baz']);
889+
$this->assertSame('corge', $page['props']['quux']);
890+
}
891+
839892
public function test_responsable_with_invalid_key(): void
840893
{
841894
$request = Request::create('/user/123', 'GET');

0 commit comments

Comments
 (0)