Skip to content

Commit 7a80e6c

Browse files
authored
[11.x] middleware support for specific method in resource routes (#53313)
* Refactor resource registration middleware handling * Refactor resource registration middleware handling and add ability to set middleware for specific methods on registered resource * Refactor singleton resource registration middleware handling and add ability to set middleware for specific methods on registered resource * Refactor middleware handling for resource registration and add support for setting middleware for specific methods on registered resources * lint * Fix documentation for middleware specification in resource registration * Enhance middleware handling to support merging middleware for specific methods on registered resources * Add support for excluding middleware for specific methods on registered resources
1 parent 832ebc0 commit 7a80e6c

File tree

4 files changed

+235
-2
lines changed

4 files changed

+235
-2
lines changed

src/Illuminate/Routing/PendingResourceRegistration.php

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -157,6 +157,41 @@ public function middleware($middleware)
157157

158158
$this->options['middleware'] = $middleware;
159159

160+
if (isset($this->options['middleware_for'])) {
161+
foreach ($this->options['middleware_for'] as $method => $value) {
162+
$this->options['middleware_for'][$method] = Router::uniqueMiddleware(array_merge(
163+
Arr::wrap($value),
164+
$middleware
165+
));
166+
}
167+
}
168+
169+
return $this;
170+
}
171+
172+
/**
173+
* Specify middleware that should be added to the specified resource routes.
174+
*
175+
* @param array|string $methods
176+
* @param array|string $middleware
177+
* @return $this
178+
*/
179+
public function middlewareFor($methods, $middleware)
180+
{
181+
$methods = Arr::wrap($methods);
182+
$middleware = Arr::wrap($middleware);
183+
184+
if (isset($this->options['middleware'])) {
185+
$middleware = Router::uniqueMiddleware(array_merge(
186+
$this->options['middleware'],
187+
$middleware
188+
));
189+
}
190+
191+
foreach ($methods as $method) {
192+
$this->options['middleware_for'][$method] = $middleware;
193+
}
194+
160195
return $this;
161196
}
162197

@@ -175,6 +210,25 @@ public function withoutMiddleware($middleware)
175210
return $this;
176211
}
177212

213+
/**
214+
* Specify middleware that should be removed from the specified resource routes.
215+
*
216+
* @param array|string $methods
217+
* @param array|string $middleware
218+
* @return $this
219+
*/
220+
public function withoutMiddlewareFor($methods, $middleware)
221+
{
222+
$methods = Arr::wrap($methods);
223+
$middleware = Arr::wrap($middleware);
224+
225+
foreach ($methods as $method) {
226+
$this->options['excluded_middleware_for'][$method] = $middleware;
227+
}
228+
229+
return $this;
230+
}
231+
178232
/**
179233
* Add "where" constraints to the resource routes.
180234
*

src/Illuminate/Routing/PendingSingletonResourceRegistration.php

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -181,6 +181,41 @@ public function middleware($middleware)
181181

182182
$this->options['middleware'] = $middleware;
183183

184+
if (isset($this->options['middleware_for'])) {
185+
foreach ($this->options['middleware_for'] as $method => $value) {
186+
$this->options['middleware_for'][$method] = Router::uniqueMiddleware(array_merge(
187+
Arr::wrap($value),
188+
$middleware
189+
));
190+
}
191+
}
192+
193+
return $this;
194+
}
195+
196+
/**
197+
* Specify middleware that should be added to the specified resource routes.
198+
*
199+
* @param array|string $methods
200+
* @param array|string $middleware
201+
* @return $this
202+
*/
203+
public function middlewareFor($methods, $middleware)
204+
{
205+
$methods = Arr::wrap($methods);
206+
$middleware = Arr::wrap($middleware);
207+
208+
if (isset($this->options['middleware'])) {
209+
$middleware = Router::uniqueMiddleware(array_merge(
210+
$this->options['middleware'],
211+
$middleware
212+
));
213+
}
214+
215+
foreach ($methods as $method) {
216+
$this->options['middleware_for'][$method] = $middleware;
217+
}
218+
184219
return $this;
185220
}
186221

@@ -199,6 +234,25 @@ public function withoutMiddleware($middleware)
199234
return $this;
200235
}
201236

237+
/**
238+
* Specify middleware that should be removed from the specified resource routes.
239+
*
240+
* @param array|string $methods
241+
* @param array|string $middleware
242+
* @return $this
243+
*/
244+
public function withoutMiddlewareFor($methods, $middleware)
245+
{
246+
$methods = Arr::wrap($methods);
247+
$middleware = Arr::wrap($middleware);
248+
249+
foreach ($methods as $method) {
250+
$this->options['excluded_middleware_for'][$method] = $middleware;
251+
}
252+
253+
return $this;
254+
}
255+
202256
/**
203257
* Add "where" constraints to the resource routes.
204258
*

src/Illuminate/Routing/ResourceRegistrar.php

Lines changed: 28 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -104,8 +104,21 @@ public function register($name, $controller, array $options = [])
104104
$resourceMethods = $this->getResourceMethods($defaults, $options);
105105

106106
foreach ($resourceMethods as $m) {
107+
$optionsForMethod = $options;
108+
109+
if (isset($optionsForMethod['middleware_for'][$m])) {
110+
$optionsForMethod['middleware'] = $optionsForMethod['middleware_for'][$m];
111+
}
112+
113+
if (isset($optionsForMethod['excluded_middleware_for'][$m])) {
114+
$optionsForMethod['excluded_middleware'] = Router::uniqueMiddleware(array_merge(
115+
$optionsForMethod['excluded_middleware'] ?? [],
116+
$optionsForMethod['excluded_middleware_for'][$m]
117+
));
118+
}
119+
107120
$route = $this->{'addResource'.ucfirst($m)}(
108-
$name, $base, $controller, $options
121+
$name, $base, $controller, $optionsForMethod
109122
);
110123

111124
if (isset($options['bindingFields'])) {
@@ -159,8 +172,21 @@ public function singleton($name, $controller, array $options = [])
159172
$resourceMethods = $this->getResourceMethods($defaults, $options);
160173

161174
foreach ($resourceMethods as $m) {
175+
$optionsForMethod = $options;
176+
177+
if (isset($optionsForMethod['middleware_for'][$m])) {
178+
$optionsForMethod['middleware'] = $optionsForMethod['middleware_for'][$m];
179+
}
180+
181+
if (isset($optionsForMethod['excluded_middleware_for'][$m])) {
182+
$optionsForMethod['excluded_middleware'] = Router::uniqueMiddleware(array_merge(
183+
$optionsForMethod['excluded_middleware'] ?? [],
184+
$optionsForMethod['excluded_middleware_for'][$m]
185+
));
186+
}
187+
162188
$route = $this->{'addSingleton'.ucfirst($m)}(
163-
$name, $controller, $options
189+
$name, $controller, $optionsForMethod
164190
);
165191

166192
if (isset($options['bindingFields'])) {

tests/Routing/RouteRegistrarTest.php

Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -844,6 +844,37 @@ public function testCanSetMiddlewareOnRegisteredResource()
844844
$this->seeMiddleware(RouteRegistrarMiddlewareStub::class);
845845
}
846846

847+
public function testCanSetMiddlewareForSpecifiedMethodsOnRegisteredResource()
848+
{
849+
$this->router->resource('users', RouteRegistrarControllerStub::class)
850+
->middleware('default')
851+
->middlewareFor('index', RouteRegistrarMiddlewareStub::class)
852+
->middlewareFor(['create', 'store'], 'one')
853+
->middlewareFor(['edit'], ['one', 'two']);
854+
855+
$this->assertEquals($this->router->getRoutes()->getByName('users.index')->gatherMiddleware(), ['default', RouteRegistrarMiddlewareStub::class]);
856+
$this->assertEquals($this->router->getRoutes()->getByName('users.create')->gatherMiddleware(), ['default', 'one']);
857+
$this->assertEquals($this->router->getRoutes()->getByName('users.store')->gatherMiddleware(), ['default', 'one']);
858+
$this->assertEquals($this->router->getRoutes()->getByName('users.show')->gatherMiddleware(), ['default']);
859+
$this->assertEquals($this->router->getRoutes()->getByName('users.edit')->gatherMiddleware(), ['default', 'one', 'two']);
860+
$this->assertEquals($this->router->getRoutes()->getByName('users.update')->gatherMiddleware(), ['default']);
861+
$this->assertEquals($this->router->getRoutes()->getByName('users.destroy')->gatherMiddleware(), ['default']);
862+
863+
$this->router->resource('users', RouteRegistrarControllerStub::class)
864+
->middlewareFor('index', RouteRegistrarMiddlewareStub::class)
865+
->middlewareFor(['create', 'store'], 'one')
866+
->middlewareFor(['edit'], ['one', 'two'])
867+
->middleware('default');
868+
869+
$this->assertEquals($this->router->getRoutes()->getByName('users.index')->gatherMiddleware(), [RouteRegistrarMiddlewareStub::class, 'default']);
870+
$this->assertEquals($this->router->getRoutes()->getByName('users.create')->gatherMiddleware(), ['one', 'default']);
871+
$this->assertEquals($this->router->getRoutes()->getByName('users.store')->gatherMiddleware(), ['one', 'default']);
872+
$this->assertEquals($this->router->getRoutes()->getByName('users.show')->gatherMiddleware(), ['default']);
873+
$this->assertEquals($this->router->getRoutes()->getByName('users.edit')->gatherMiddleware(), ['one', 'two', 'default']);
874+
$this->assertEquals($this->router->getRoutes()->getByName('users.update')->gatherMiddleware(), ['default']);
875+
$this->assertEquals($this->router->getRoutes()->getByName('users.destroy')->gatherMiddleware(), ['default']);
876+
}
877+
847878
public function testResourceWithoutMiddlewareRegistration()
848879
{
849880
$this->router->resource('users', RouteRegistrarControllerStub::class)
@@ -856,6 +887,23 @@ public function testResourceWithoutMiddlewareRegistration()
856887
$this->assertEquals(['one'], $this->getRoute()->excludedMiddleware());
857888
}
858889

890+
public function testCanSetExcludedMiddlewareForSpecifiedMethodsOnRegisteredResource()
891+
{
892+
$this->router->resource('users', RouteRegistrarControllerStub::class)
893+
->withoutMiddleware('one')
894+
->withoutMiddlewareFor('index', 'two')
895+
->withoutMiddlewareFor(['create', 'store'], 'three')
896+
->withoutMiddlewareFor(['edit'], ['four', 'five']);
897+
898+
$this->assertEquals($this->router->getRoutes()->getByName('users.index')->excludedMiddleware(), ['one', 'two']);
899+
$this->assertEquals($this->router->getRoutes()->getByName('users.create')->excludedMiddleware(), ['one', 'three']);
900+
$this->assertEquals($this->router->getRoutes()->getByName('users.store')->excludedMiddleware(), ['one', 'three']);
901+
$this->assertEquals($this->router->getRoutes()->getByName('users.show')->excludedMiddleware(), ['one']);
902+
$this->assertEquals($this->router->getRoutes()->getByName('users.edit')->excludedMiddleware(), ['one', 'four', 'five']);
903+
$this->assertEquals($this->router->getRoutes()->getByName('users.update')->excludedMiddleware(), ['one']);
904+
$this->assertEquals($this->router->getRoutes()->getByName('users.destroy')->excludedMiddleware(), ['one']);
905+
}
906+
859907
public function testResourceWithMiddlewareAsStringable()
860908
{
861909
$one = new class implements Stringable
@@ -1338,6 +1386,57 @@ public function testApiSingletonCanIncludeAnySingletonMethods()
13381386
$this->assertTrue($this->router->getRoutes()->hasNamedRoute('user.edit'));
13391387
}
13401388

1389+
public function testCanSetMiddlewareForSpecifiedMethodsOnRegisteredSingletonResource()
1390+
{
1391+
$this->router->singleton('users', RouteRegistrarControllerStub::class)
1392+
->creatable()
1393+
->destroyable()
1394+
->middleware('default')
1395+
->middlewareFor('show', RouteRegistrarMiddlewareStub::class)
1396+
->middlewareFor(['create', 'store'], 'one')
1397+
->middlewareFor(['edit'], ['one', 'two']);
1398+
1399+
$this->assertEquals($this->router->getRoutes()->getByName('users.create')->gatherMiddleware(), ['default', 'one']);
1400+
$this->assertEquals($this->router->getRoutes()->getByName('users.store')->gatherMiddleware(), ['default', 'one']);
1401+
$this->assertEquals($this->router->getRoutes()->getByName('users.show')->gatherMiddleware(), ['default', RouteRegistrarMiddlewareStub::class]);
1402+
$this->assertEquals($this->router->getRoutes()->getByName('users.edit')->gatherMiddleware(), ['default', 'one', 'two']);
1403+
$this->assertEquals($this->router->getRoutes()->getByName('users.update')->gatherMiddleware(), ['default']);
1404+
$this->assertEquals($this->router->getRoutes()->getByName('users.destroy')->gatherMiddleware(), ['default']);
1405+
1406+
$this->router->singleton('users', RouteRegistrarControllerStub::class)
1407+
->creatable()
1408+
->destroyable()
1409+
->middlewareFor('show', RouteRegistrarMiddlewareStub::class)
1410+
->middlewareFor(['create', 'store'], 'one')
1411+
->middlewareFor(['edit'], ['one', 'two'])
1412+
->middleware('default');
1413+
1414+
$this->assertEquals($this->router->getRoutes()->getByName('users.create')->gatherMiddleware(), ['one', 'default']);
1415+
$this->assertEquals($this->router->getRoutes()->getByName('users.store')->gatherMiddleware(), ['one', 'default']);
1416+
$this->assertEquals($this->router->getRoutes()->getByName('users.show')->gatherMiddleware(), [RouteRegistrarMiddlewareStub::class, 'default']);
1417+
$this->assertEquals($this->router->getRoutes()->getByName('users.edit')->gatherMiddleware(), ['one', 'two', 'default']);
1418+
$this->assertEquals($this->router->getRoutes()->getByName('users.update')->gatherMiddleware(), ['default']);
1419+
$this->assertEquals($this->router->getRoutes()->getByName('users.destroy')->gatherMiddleware(), ['default']);
1420+
}
1421+
1422+
public function testCanSetExcludedMiddlewareForSpecifiedMethodsOnRegisteredSingletonResource()
1423+
{
1424+
$this->router->singleton('users', RouteRegistrarControllerStub::class)
1425+
->creatable()
1426+
->destroyable()
1427+
->withoutMiddleware('one')
1428+
->withoutMiddlewareFor('show', 'two')
1429+
->withoutMiddlewareFor(['create', 'store'], 'three')
1430+
->withoutMiddlewareFor(['edit'], ['four', 'five']);
1431+
1432+
$this->assertEquals($this->router->getRoutes()->getByName('users.create')->excludedMiddleware(), ['one', 'three']);
1433+
$this->assertEquals($this->router->getRoutes()->getByName('users.store')->excludedMiddleware(), ['one', 'three']);
1434+
$this->assertEquals($this->router->getRoutes()->getByName('users.show')->excludedMiddleware(), ['one', 'two']);
1435+
$this->assertEquals($this->router->getRoutes()->getByName('users.edit')->excludedMiddleware(), ['one', 'four', 'five']);
1436+
$this->assertEquals($this->router->getRoutes()->getByName('users.update')->excludedMiddleware(), ['one']);
1437+
$this->assertEquals($this->router->getRoutes()->getByName('users.destroy')->excludedMiddleware(), ['one']);
1438+
}
1439+
13411440
/**
13421441
* Get the last route registered with the router.
13431442
*

0 commit comments

Comments
 (0)