Skip to content

Commit a516597

Browse files
authored
Merge pull request #161 from claudiodekker/rich-middleware
Inertia Middleware: A better home for user-provided defaults.
2 parents 3db77cb + 9902fed commit a516597

11 files changed

+415
-184
lines changed

src/Console/CreateMiddleware.php

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
<?php
2+
3+
namespace Inertia\Console;
4+
5+
use Illuminate\Console\GeneratorCommand;
6+
7+
class CreateMiddleware extends GeneratorCommand
8+
{
9+
/**
10+
* The name and signature of the console command.
11+
*
12+
* @var string
13+
*/
14+
protected $signature = 'inertia:middleware {name=HandleInertiaRequests : Name of the Middleware that should be created}';
15+
16+
/**
17+
* The console command description.
18+
*
19+
* @var string
20+
*/
21+
protected $description = 'Creates a new Inertia middleware';
22+
23+
/**
24+
* The type of class being generated.
25+
*
26+
* @var string
27+
*/
28+
protected $type = 'Middleware';
29+
30+
/**
31+
* Get the stub file for the generator.
32+
*
33+
* @return string
34+
*/
35+
protected function getStub()
36+
{
37+
return __DIR__.'/../../stubs/middleware.stub';
38+
}
39+
40+
/**
41+
* Get the default namespace for the class.
42+
*
43+
* @param string $rootNamespace
44+
* @return string
45+
*/
46+
protected function getDefaultNamespace($rootNamespace)
47+
{
48+
return $rootNamespace.'\Http\Middleware';
49+
}
50+
}

src/Middleware.php

Lines changed: 108 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -3,31 +3,130 @@
33
namespace Inertia;
44

55
use Closure;
6-
use Illuminate\Support\Facades\Response;
7-
use Symfony\Component\HttpFoundation\RedirectResponse as Redirect;
6+
use Illuminate\Http\Request;
7+
use Symfony\Component\HttpFoundation\Response;
88

99
class Middleware
1010
{
11-
public function handle($request, Closure $next)
11+
/**
12+
* Determines the current asset version.
13+
*
14+
* @see https://inertiajs.com/asset-versioning
15+
* @param \Illuminate\Http\Request $request
16+
* @return string|null
17+
*/
18+
public function version(Request $request)
1219
{
13-
$response = $next($request);
20+
if (config('app.asset_url')) {
21+
return md5(config('app.asset_url'));
22+
}
1423

15-
if (!$request->header('X-Inertia')) {
16-
return $response;
24+
if (file_exists($manifest = public_path('mix-manifest.json'))) {
25+
return md5_file($manifest);
1726
}
27+
}
28+
29+
/**
30+
* Defines the props that are shared by default.
31+
*
32+
* @see https://inertiajs.com/shared-data
33+
* @param \Illuminate\Http\Request $request
34+
* @return array
35+
*/
36+
public function share(Request $request)
37+
{
38+
return [
39+
'errors' => function () use ($request) {
40+
return $this->resolveValidationErrors($request);
41+
},
42+
];
43+
}
44+
45+
/**
46+
* Handle the incoming request.
47+
*
48+
* @param \Illuminate\Http\Request $request
49+
* @param Closure $next
50+
* @return Response
51+
*/
52+
public function handle(Request $request, Closure $next)
53+
{
54+
Inertia::version(function () use ($request) {
55+
return $this->version($request);
56+
});
57+
58+
Inertia::share($this->share($request));
59+
60+
$response = $next($request);
61+
$response = $this->checkVersion($request, $response);
62+
$response = $this->changeRedirectCode($request, $response);
63+
64+
return $response;
65+
}
1866

19-
if ($request->method() === 'GET' && $request->header('X-Inertia-Version', '') !== Inertia::getVersion()) {
67+
/**
68+
* In the event that the assets change, initiate a
69+
* client-side location visit to force an update.
70+
*
71+
* @param Request $request
72+
* @param Response $response
73+
* @return Response
74+
*/
75+
public function checkVersion(Request $request, Response $response)
76+
{
77+
if ($request->header('X-Inertia') &&
78+
$request->method() === 'GET' &&
79+
$request->header('X-Inertia-Version', '') !== Inertia::getVersion()
80+
) {
2081
if ($request->hasSession()) {
2182
$request->session()->reflash();
2283
}
2384

24-
return Response::make('', 409, ['X-Inertia-Location' => $request->fullUrl()]);
85+
return Inertia::location($request->fullUrl());
2586
}
2687

27-
if ($response instanceof Redirect && $response->getStatusCode() === 302 && in_array($request->method(), ['PUT', 'PATCH', 'DELETE'])) {
88+
return $response;
89+
}
90+
91+
/**
92+
* Changes the status code during redirects, ensuring they are made as
93+
* GET requests, preventing "MethodNotAllowedHttpException" errors.
94+
*
95+
* @param Request $request
96+
* @param Response $response
97+
* @return Response
98+
*/
99+
public function changeRedirectCode(Request $request, Response $response)
100+
{
101+
if ($request->header('X-Inertia') &&
102+
$response->getStatusCode() === 302 &&
103+
in_array($request->method(), ['PUT', 'PATCH', 'DELETE'])
104+
) {
28105
$response->setStatusCode(303);
29106
}
30107

31108
return $response;
32109
}
110+
111+
/**
112+
* Resolves and prepares validation errors in such
113+
* a way that they are easier to use client-side.
114+
*
115+
* @param Request $request
116+
* @return object
117+
*/
118+
public function resolveValidationErrors(Request $request)
119+
{
120+
if (! $request->session()->has('errors')) {
121+
return (object) [];
122+
}
123+
124+
return (object) collect($request->session()->get('errors')->getBags())->map(function ($bag) {
125+
return (object) collect($bag->messages())->map(function ($errors) {
126+
return $errors[0];
127+
})->toArray();
128+
})->pipe(function ($bags) {
129+
return $bags->has('default') ? $bags->get('default') : $bags->toArray();
130+
});
131+
}
33132
}

src/ServiceProvider.php

Lines changed: 12 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,7 @@
44

55
use Illuminate\Http\Request;
66
use Illuminate\Routing\Router;
7-
use Illuminate\Support\Collection;
8-
use Illuminate\Contracts\Http\Kernel;
97
use Illuminate\Support\Facades\Blade;
10-
use Illuminate\Support\Facades\Config;
11-
use Illuminate\Support\Facades\Session;
128
use Illuminate\Support\ServiceProvider as BaseServiceProvider;
139

1410
class ServiceProvider extends BaseServiceProvider
@@ -21,10 +17,9 @@ public function register()
2117
public function boot()
2218
{
2319
$this->registerBladeDirective();
20+
$this->registerConsoleCommands();
2421
$this->registerRequestMacro();
2522
$this->registerRouterMacro();
26-
$this->registerMiddleware();
27-
$this->shareValidationErrors();
2823
}
2924

3025
protected function registerBladeDirective()
@@ -34,6 +29,17 @@ protected function registerBladeDirective()
3429
});
3530
}
3631

32+
protected function registerConsoleCommands()
33+
{
34+
if (! $this->app->runningInConsole()) {
35+
return;
36+
}
37+
38+
$this->commands([
39+
Console\CreateMiddleware::class,
40+
]);
41+
}
42+
3743
protected function registerRequestMacro()
3844
{
3945
Request::macro('inertia', function () {
@@ -49,40 +55,4 @@ protected function registerRouterMacro()
4955
->defaults('props', $props);
5056
});
5157
}
52-
53-
protected function registerMiddleware()
54-
{
55-
$kernel = $this->app[Kernel::class];
56-
$group = Config::get('inertia.middleware_group', 'web');
57-
58-
// Laravel >= 6.9.0
59-
if (method_exists($kernel, 'appendMiddlewareToGroup')) {
60-
$kernel->appendMiddlewareToGroup($group, Middleware::class);
61-
62-
// Laravel >= 5.4.4 && < 6.9.0
63-
} elseif ($this->app[Router::class]->hasMiddlewareGroup($group)) {
64-
$this->app[Router::class]->pushMiddlewareToGroup($group, Middleware::class);
65-
}
66-
}
67-
68-
protected function shareValidationErrors()
69-
{
70-
if (Inertia::getShared('errors')) {
71-
return;
72-
}
73-
74-
Inertia::share('errors', function () {
75-
if (! Session::has('errors')) {
76-
return (object) [];
77-
}
78-
79-
return (object) Collection::make(Session::get('errors')->getBags())->map(function ($bag) {
80-
return (object) Collection::make($bag->messages())->map(function ($errors) {
81-
return $errors[0];
82-
})->toArray();
83-
})->pipe(function ($bags) {
84-
return $bags->has('default') ? $bags->get('default') : $bags->toArray();
85-
});
86-
});
87-
}
8858
}

stubs/middleware.stub

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
<?php
2+
3+
namespace {{ namespace }};
4+
5+
use Illuminate\Http\Request;
6+
use Inertia\Middleware;
7+
8+
class {{ class }} extends Middleware
9+
{
10+
/**
11+
* Determines the current asset version.
12+
*
13+
* @see https://inertiajs.com/asset-versioning
14+
* @param \Illuminate\Http\Request $request
15+
* @return string|null
16+
*/
17+
public function version(Request $request)
18+
{
19+
return parent::version($request);
20+
}
21+
22+
/**
23+
* Defines the props that are shared by default.
24+
*
25+
* @see https://inertiajs.com/shared-data
26+
* @param \Illuminate\Http\Request $request
27+
* @return array
28+
*/
29+
public function share(Request $request)
30+
{
31+
return array_merge(parent::share($request), [
32+
//
33+
]);
34+
}
35+
}

tests/ControllerTest.php

Lines changed: 17 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -2,39 +2,33 @@
22

33
namespace Inertia\Tests;
44

5-
use Inertia\Response;
5+
use Illuminate\Session\Middleware\StartSession;
6+
use Illuminate\Support\Facades\Route;
67
use Inertia\Controller;
7-
use Illuminate\Http\Request;
8-
use Illuminate\Routing\Route;
8+
use Inertia\Tests\Stubs\ExampleMiddleware;
99

1010
class ControllerTest extends TestCase
1111
{
1212
public function test_controller_returns_an_inertia_response()
1313
{
14-
$request = new Request();
15-
$request->setRouteResolver(static function () {
16-
$route = new Route(['GET'], '/', ['\Inertia\Controller', '__invoke']);
17-
$route->defaults('component', 'User/Edit');
18-
$route->defaults('props', [
14+
Route::middleware([StartSession::class, ExampleMiddleware::class])
15+
->get('/', Controller::class)
16+
->defaults('component', 'User/Edit')
17+
->defaults('props', [
1918
'user' => ['name' => 'Jonathan'],
2019
]);
2120

22-
return $route;
23-
});
21+
$response = $this->get('/');
2422

25-
$response = (new Controller())($request);
26-
27-
$this->assertInstanceOf(Response::class, $response);
28-
$this->assertEquals([
29-
'page' => [
30-
'component' => 'User/Edit',
31-
'props' => [
32-
'user' => ['name' => 'Jonathan'],
33-
'errors' => (object) [],
34-
],
35-
'url' => '',
36-
'version' => null,
23+
$page = $response->viewData('page');
24+
$this->assertEquals($page, [
25+
'component' => 'User/Edit',
26+
'props' => [
27+
'user' => ['name' => 'Jonathan'],
28+
'errors' => (object) [],
3729
],
38-
], $response->toResponse(new Request())->getOriginalContent()->getData());
30+
'url' => '/',
31+
'version' => '',
32+
]);
3933
}
4034
}

0 commit comments

Comments
 (0)