Skip to content
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

Add "always" props using new Inertia::always() wrapper #627

Merged
merged 6 commits into from
May 25, 2024
Merged
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
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@

## [Unreleased](https://github.com/inertiajs/inertia-laravel/compare/v1.2.0...1.x)

- Add "always" props using new `Inertia::always()` wrapper ([#627](https://github.com/inertiajs/inertia-laravel/pull/627))

## [v1.2.0](https://github.com/inertiajs/inertia-laravel/compare/v1.1.0...v1.2.0) - 2024-05-17

* [1.x] Make commands lazy by [@timacdonald](https://github.com/timacdonald) in https://github.com/inertiajs/inertia-laravel/pull/601
Expand Down
24 changes: 24 additions & 0 deletions src/AlwaysProp.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
<?php

namespace Inertia;

use Illuminate\Support\Facades\App;

class AlwaysProp
{
/** @var mixed */
protected $value;

/**
* @param mixed $value
*/
public function __construct($value)
{
$this->value = $value;
}

public function __invoke()
{
return is_callable($this->value) ? App::call($this->value) : $this->value;
}
}
4 changes: 1 addition & 3 deletions src/Inertia.php
Original file line number Diff line number Diff line change
Expand Up @@ -12,15 +12,13 @@
* @method static void version(\Closure|string|null $version)
* @method static string getVersion()
* @method static \Inertia\LazyProp lazy(callable $callback)
* @method static \Inertia\AlwaysProp always(mixed $value)
* @method static \Inertia\Response render(string $component, array|\Illuminate\Contracts\Support\Arrayable $props = [])
* @method static \Symfony\Component\HttpFoundation\Response location(string|\Symfony\Component\HttpFoundation\RedirectResponse $url)
* @method static void macro(string $name, object|callable $macro)
* @method static void mixin(object $mixin, bool $replace = true)
* @method static bool hasMacro(string $name)
* @method static void flushMacros()
* @method static void persist(string|array|\Illuminate\Contracts\Support\Arrayable $props)
* @method static array getPersisted()
* @method static void flushPersisted()
*
* @see \Inertia\ResponseFactory
*/
Expand Down
12 changes: 1 addition & 11 deletions src/Middleware.php
Original file line number Diff line number Diff line change
Expand Up @@ -19,13 +19,6 @@ class Middleware
*/
protected $rootView = 'app';

/**
* The properties that should always be included on Inertia responses, regardless of "only" or "except" requests.
*
* @var array
*/
protected $persisted = [];

/**
* Determines the current asset version.
*
Expand Down Expand Up @@ -60,9 +53,7 @@ public function version(Request $request)
public function share(Request $request)
{
return [
'errors' => function () use ($request) {
return $this->resolveValidationErrors($request);
},
'errors' => Inertia::always($this->resolveValidationErrors($request)),

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this is a breaking change, isnt it? @lepikhinb

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Shouldn't be

];
}

Expand Down Expand Up @@ -90,7 +81,6 @@ public function handle(Request $request, Closure $next)
});

Inertia::share($this->share($request));
Inertia::persist($this->persisted);
Inertia::setRootView($this->rootView($request));

$response = $next($request);
Expand Down
30 changes: 23 additions & 7 deletions src/Response.php
Original file line number Diff line number Diff line change
Expand Up @@ -23,19 +23,17 @@ class Response implements Responsable

protected $component;
protected $props;
protected $persisted;
protected $rootView;
protected $version;
protected $viewData = [];

/**
* @param array|Arrayable $props
*/
public function __construct(string $component, array $props, string $rootView = 'app', string $version = '', array $persisted = [])
public function __construct(string $component, array $props, string $rootView = 'app', string $version = '')
{
$this->component = $component;
$this->props = $props instanceof Arrayable ? $props->toArray() : $props;
$this->persisted = $persisted;
$this->rootView = $rootView;
$this->version = $version;
}
Expand Down Expand Up @@ -129,6 +127,8 @@ public function resolveProperties(Request $request, array $props): array
$props = $this->resolveExcept($request, $props);
}

$props = $this->resolveAlways($props);

$props = $this->resolvePropertyInstances($props, $request);

return $props;
Expand Down Expand Up @@ -164,10 +164,7 @@ public function resolveArrayableProperties(array $props, Request $request, bool
*/
public function resolveOnly(Request $request, array $props): array
{
$only = array_merge(
array_filter(explode(',', $request->header(Header::PARTIAL_ONLY, ''))),
$this->persisted
);
$only = array_filter(explode(',', $request->header(Header::PARTIAL_ONLY, '')));

$value = [];

Expand All @@ -190,6 +187,21 @@ public function resolveExcept(Request $request, array $props): array
return $props;
}

/**
* Resolve `always` properties that should always be included on all visits, regardless of "only" or "except" requests.
*/
public function resolveAlways(array $props): array
{
$always = array_filter($this->props, static function ($prop) {
return $prop instanceof AlwaysProp;
});

return array_merge(
$always,
$props
);
}

/**
* Resolve all necessary class instances in the given props.
*/
Expand All @@ -204,6 +216,10 @@ public function resolvePropertyInstances(array $props, Request $request): array
$value = App::call($value);
}

if ($value instanceof AlwaysProp) {
$value = App::call($value);
}

if ($value instanceof PromiseInterface) {
$value = $value->wait();
}
Expand Down
38 changes: 9 additions & 29 deletions src/ResponseFactory.php
Original file line number Diff line number Diff line change
Expand Up @@ -24,9 +24,6 @@ class ResponseFactory
/** @var array */
protected $sharedProps = [];

/** @var array */
protected $persisted = [];

/** @var Closure|string|null */
protected $version;

Expand Down Expand Up @@ -69,30 +66,6 @@ public function flushShared(): void
$this->sharedProps = [];
}

/**
* @param string|array|Arrayable $props
*/
public function persist($props): void
{
if (is_array($props)) {
$this->persisted = array_merge($this->persisted, $props);
} elseif ($props instanceof Arrayable) {
$this->persisted = array_merge($this->persisted, $props->toArray());
} else {
$this->persisted[] = $props;
}
}

public function getPersisted(): array
{
return $this->persisted;
}

public function flushPersisted(): void
{
$this->persisted = [];
}

/**
* @param Closure|string|null $version
*/
Expand All @@ -115,6 +88,14 @@ public function lazy(callable $callback): LazyProp
return new LazyProp($callback);
}

/**
* @param mixed $value
*/
public function always($value): AlwaysProp
{
return new AlwaysProp($value);
}

/**
* @param array|Arrayable $props
*/
Expand All @@ -128,8 +109,7 @@ public function render(string $component, $props = []): Response
$component,
array_merge($this->sharedProps, $props),
$this->rootView,
$this->getVersion(),
$this->persisted
$this->getVersion()
);
}

Expand Down
47 changes: 47 additions & 0 deletions tests/AlwaysPropTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
<?php

namespace Inertia\Tests;

use Illuminate\Http\Request;
use Inertia\AlwaysProp;

class AlwaysPropTest extends TestCase
{
public function test_can_invoke(): void
{
$alwaysProp = new AlwaysProp(function () {
return 'An always value';
});

$this->assertSame('An always value', $alwaysProp());
}

public function test_can_accept_scalar_values(): void
{
$alwaysProp = new AlwaysProp('An always value');

$this->assertSame('An always value', $alwaysProp());
}

public function test_can_accept_callables(): void
{
$callable = new class {
public function __invoke() {
return 'An always value';
}
};

$alwaysProp = new AlwaysProp($callable);

$this->assertSame('An always value', $alwaysProp());
}

public function test_can_resolve_bindings_when_invoked(): void
{
$alwaysProp = new AlwaysProp(function (Request $request) {
return $request;
});

$this->assertInstanceOf(Request::class, $alwaysProp());
}
}
37 changes: 4 additions & 33 deletions tests/MiddlewareTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@

namespace Inertia\Tests;

use Closure;
use LogicException;
use Inertia\Inertia;
use Inertia\Middleware;
Expand All @@ -13,6 +12,7 @@
use Illuminate\Support\Facades\Session;
use Inertia\Tests\Stubs\ExampleMiddleware;
use Illuminate\Session\Middleware\StartSession;
use Inertia\AlwaysProp;

class MiddlewareTest extends TestCase
{
Expand Down Expand Up @@ -125,7 +125,7 @@ public function test_it_will_instruct_inertia_to_reload_on_a_version_mismatch():
public function test_validation_errors_are_registered_as_of_default(): void
{
Route::middleware([StartSession::class, ExampleMiddleware::class])->get('/', function () {
$this->assertInstanceOf(Closure::class, Inertia::getShared('errors'));
$this->assertInstanceOf(AlwaysProp::class, Inertia::getShared('errors'));
});

$this->withoutExceptionHandling()->get('/');
Expand Down Expand Up @@ -239,39 +239,10 @@ public function rootView(Request $request): string
$response->assertViewIs('welcome');
}

public function test_middleware_can_set_persisted_properties(): void
{
$shared = [
'shared' => [
'flash' => 'The user has been updated.'
]
];

$this->prepareMockEndpoint(null, $shared, null, ['shared']);

$response = $this->get('/', [
'X-Inertia' => 'true',
'X-Inertia-Partial-Component' => 'User/Edit',
'X-Inertia-Partial-Data' => 'user'
]);

$response->assertOk();
$response->assertJson([
'props' => [
'shared' => [
'flash' => 'The user has been updated.'
],
'user' => [
'name' => 'Jonathan',
]
]
]);
}

private function prepareMockEndpoint($version = null, $shared = [], $middleware = null, $persisted = []): \Illuminate\Routing\Route
private function prepareMockEndpoint($version = null, $shared = [], $middleware = null): \Illuminate\Routing\Route
{
if (is_null($middleware)) {
$middleware = new ExampleMiddleware($version, $shared, $persisted);
$middleware = new ExampleMiddleware($version, $shared);
}

return Route::middleware(StartSession::class)->get('/', function (Request $request) use ($middleware) {
Expand Down
27 changes: 11 additions & 16 deletions tests/ResponseFactoryTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
use Illuminate\Session\Middleware\StartSession;
use Illuminate\Session\NullSessionHandler;
use Illuminate\Session\Store;
use Inertia\AlwaysProp;

class ResponseFactoryTest extends TestCase
{
Expand Down Expand Up @@ -150,22 +151,6 @@ public function test_can_flush_shared_data(): void
$this->assertSame([], Inertia::getShared());
}

public function test_can_persist_properties(): void
{
Inertia::persist('auth.user');
$this->assertSame(['auth.user'], Inertia::getPersisted());
Inertia::persist(['posts']);
$this->assertSame(['auth.user', 'posts'], Inertia::getPersisted());
}

public function test_can_flush_persisted_data(): void
{
Inertia::persist('auth.user');
$this->assertSame(['auth.user'], Inertia::getPersisted());
Inertia::flushPersisted();
$this->assertSame([], Inertia::getPersisted());
}

public function test_can_create_lazy_prop(): void
{
$factory = new ResponseFactory();
Expand All @@ -176,6 +161,16 @@ public function test_can_create_lazy_prop(): void
$this->assertInstanceOf(LazyProp::class, $lazyProp);
}

public function test_can_create_always_prop(): void
{
$factory = new ResponseFactory();
$alwaysProp = $factory->always(function () {
return 'An always value';
});

$this->assertInstanceOf(AlwaysProp::class, $alwaysProp);
}

public function test_will_accept_arrayabe_props()
{
Route::middleware([StartSession::class, ExampleMiddleware::class])->get('/', function () {
Expand Down
Loading
Loading