Skip to content

Commit

Permalink
Add "always" props using new Inertia::always() wrapper (#627)
Browse files Browse the repository at this point in the history
* `AlwaysProp` implementation

* make `always` accept any param type

* resolve bindings on `AlwaysProp` invoke

* resolve callables in `AlwaysProp`

* Update "always" types in facade

* Update changelog

---------

Co-authored-by: Jonathan Reinink <[email protected]>
  • Loading branch information
lepikhinb and reinink authored May 25, 2024
1 parent 82f3dc6 commit 5cbaeab
Show file tree
Hide file tree
Showing 11 changed files with 140 additions and 124 deletions.
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)),
];
}

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

0 comments on commit 5cbaeab

Please sign in to comment.