Skip to content

[2.x] Introduce ProvidesInertiaProps interface #748

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 2 commits into
base: inertia-responsable-prop-type
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions src/ProvidesInertiaProps.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
<?php

namespace Inertia;

interface ProvidesInertiaProps
{
public function toInertiaProps(RenderContext $context): iterable;
}
15 changes: 15 additions & 0 deletions src/RenderContext.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
<?php

namespace Inertia;

use Illuminate\Http\Request;

class RenderContext
{
public function __construct(
public string $component,
public Request $request
) {
//
}
}
33 changes: 30 additions & 3 deletions src/Response.php
Original file line number Diff line number Diff line change
Expand Up @@ -50,13 +50,15 @@ public function __construct(string $component, array $props, string $rootView =
}

/**
* @param string|array $key
* @param string|array|ProvidesInertiaProps $key
* @param mixed $value
* @return $this
*/
public function with($key, $value = null): self
{
if (is_array($key)) {
if ($key instanceof ProvidesInertiaProps) {
$this->props[] = $key;
} elseif (is_array($key)) {
$this->props = array_merge($this->props, $key);
} else {
$this->props[$key] = $value;
Expand Down Expand Up @@ -131,6 +133,7 @@ public function toResponse($request)
*/
public function resolveProperties(Request $request, array $props): array
{
$props = $this->resolveInertiaPropsProviders($props, $request);
$props = $this->resolvePartialProperties($props, $request);
$props = $this->resolveArrayableProperties($props, $request);
$props = $this->resolveAlways($props);
Expand All @@ -139,13 +142,37 @@ public function resolveProperties(Request $request, array $props): array
return $props;
}

/**
* Resolve the ProvidesInertiaProps props.
*/
public function resolveInertiaPropsProviders(array $props, Request $request): array
{
$newProps = [];

$renderContext = new RenderContext($this->component, $request);

foreach ($props as $key => $value) {
if (is_numeric($key) && $value instanceof ProvidesInertiaProps) {
// Pipe into a Collection to leverage Collection::getArrayableItems()
$newProps = array_merge(
$newProps,
collect($value->toInertiaProps($renderContext))->all()
);
} else {
$newProps[$key] = $value;
}
}

return $newProps;
}

/**
* Resolve the `only` and `except` partial request props.
*/
public function resolvePartialProperties(array $props, Request $request): array
{
if (! $this->isPartial($request)) {
return array_filter($this->props, static function ($prop) {
return array_filter($props, static function ($prop) {
return ! ($prop instanceof IgnoreFirstLoad);
});
}
Expand Down
5 changes: 4 additions & 1 deletion src/ResponseFactory.php
Original file line number Diff line number Diff line change
Expand Up @@ -151,12 +151,15 @@ public function always($value): AlwaysProp
}

/**
* @param array|Arrayable $props
* @param array|Arrayable|ProvidesInertiaProps $props
*/
public function render(string $component, $props = []): Response
{
if ($props instanceof Arrayable) {
$props = $props->toArray();
} elseif ($props instanceof ProvidesInertiaProps) {
// Will be resolved in Response::resolveResponsableProperties()
$props = [$props];
}

return new Response(
Expand Down
26 changes: 26 additions & 0 deletions tests/ResponseFactoryTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@
use Inertia\LazyProp;
use Inertia\MergeProp;
use Inertia\OptionalProp;
use Inertia\ProvidesInertiaProps;
use Inertia\RenderContext;
use Inertia\ResponseFactory;
use Inertia\Tests\Stubs\ExampleMiddleware;

Expand Down Expand Up @@ -352,4 +354,28 @@ public function toArray()
],
]);
}

public function test_will_accept_instances_of_provides_inertia_props()
{
Route::middleware([StartSession::class, ExampleMiddleware::class])->get('/', function () {
return Inertia::render('User/Edit', new class implements ProvidesInertiaProps
{
public function toInertiaProps(RenderContext $context): iterable
{
return [
'foo' => 'bar',
];
}
});
});

$response = $this->withoutExceptionHandling()->get('/', ['X-Inertia' => 'true']);
$response->assertSuccessful();
$response->assertJson([
'component' => 'User/Edit',
'props' => [
'foo' => 'bar',
],
]);
}
}
53 changes: 53 additions & 0 deletions tests/ResponseTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@
use Inertia\Inertia;
use Inertia\LazyProp;
use Inertia\MergeProp;
use Inertia\ProvidesInertiaProps;
use Inertia\RenderContext;
use Inertia\Response;
use Inertia\Tests\Stubs\FakeResource;
use Inertia\Tests\Stubs\MergeWithSharedProp;
Expand Down Expand Up @@ -759,6 +761,33 @@ public function test_always_props_are_included_on_partial_reload(): void
$this->assertFalse(isset($page->props->user));
}

public function test_inertia_responsable_objects(): void
{
$request = Request::create('/user/123', 'GET');

$response = new Response('User/Edit', [
'foo' => 'bar',
new class implements ProvidesInertiaProps
{
public function toInertiaProps(RenderContext $context): iterable
{
return collect([
'baz' => 'qux',
]);
}
},
'quux' => 'corge',

], 'app', '123');
$response = $response->toResponse($request);
$view = $response->getOriginalContent();
$page = $view->getData()['page'];

$this->assertSame('bar', $page['props']['foo']);
$this->assertSame('qux', $page['props']['baz']);
$this->assertSame('corge', $page['props']['quux']);
}

public function test_inertia_response_type_prop(): void
{
$request = Request::create('/user/123', 'GET');
Expand Down Expand Up @@ -836,6 +865,30 @@ public function test_nested_dot_props_do_not_get_unpacked(): void
$this->assertFalse(array_key_exists('can', $auth));
}

public function test_props_can_be_added_using_the_with_method(): void
{
$request = Request::create('/user/123', 'GET');
$response = new Response('User/Edit', [], 'app', '123');

$response->with(['foo' => 'bar', 'baz' => 'qux'])
->with(['quux' => 'corge'])
->with(new class implements ProvidesInertiaProps
{
public function toInertiaProps(RenderContext $context): iterable
{
return collect(['grault' => 'garply']);
}
});

$response = $response->toResponse($request);
$view = $response->getOriginalContent();
$page = $view->getData()['page'];

$this->assertSame('bar', $page['props']['foo']);
$this->assertSame('qux', $page['props']['baz']);
$this->assertSame('corge', $page['props']['quux']);
}

public function test_responsable_with_invalid_key(): void
{
$request = Request::create('/user/123', 'GET');
Expand Down