Skip to content

Commit

Permalink
[11.x] middleware support for specific method in resource routes (#53313
Browse files Browse the repository at this point in the history
)

* 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
  • Loading branch information
MrPunyapal authored Jan 9, 2025
1 parent 832ebc0 commit 7a80e6c
Show file tree
Hide file tree
Showing 4 changed files with 235 additions and 2 deletions.
54 changes: 54 additions & 0 deletions src/Illuminate/Routing/PendingResourceRegistration.php
Original file line number Diff line number Diff line change
Expand Up @@ -157,6 +157,41 @@ public function middleware($middleware)

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

if (isset($this->options['middleware_for'])) {
foreach ($this->options['middleware_for'] as $method => $value) {
$this->options['middleware_for'][$method] = Router::uniqueMiddleware(array_merge(
Arr::wrap($value),
$middleware
));
}
}

return $this;
}

/**
* Specify middleware that should be added to the specified resource routes.
*
* @param array|string $methods
* @param array|string $middleware
* @return $this
*/
public function middlewareFor($methods, $middleware)
{
$methods = Arr::wrap($methods);
$middleware = Arr::wrap($middleware);

if (isset($this->options['middleware'])) {
$middleware = Router::uniqueMiddleware(array_merge(
$this->options['middleware'],
$middleware
));
}

foreach ($methods as $method) {
$this->options['middleware_for'][$method] = $middleware;
}

return $this;
}

Expand All @@ -175,6 +210,25 @@ public function withoutMiddleware($middleware)
return $this;
}

/**
* Specify middleware that should be removed from the specified resource routes.
*
* @param array|string $methods
* @param array|string $middleware
* @return $this
*/
public function withoutMiddlewareFor($methods, $middleware)
{
$methods = Arr::wrap($methods);
$middleware = Arr::wrap($middleware);

foreach ($methods as $method) {
$this->options['excluded_middleware_for'][$method] = $middleware;
}

return $this;
}

/**
* Add "where" constraints to the resource routes.
*
Expand Down
54 changes: 54 additions & 0 deletions src/Illuminate/Routing/PendingSingletonResourceRegistration.php
Original file line number Diff line number Diff line change
Expand Up @@ -181,6 +181,41 @@ public function middleware($middleware)

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

if (isset($this->options['middleware_for'])) {
foreach ($this->options['middleware_for'] as $method => $value) {
$this->options['middleware_for'][$method] = Router::uniqueMiddleware(array_merge(
Arr::wrap($value),
$middleware
));
}
}

return $this;
}

/**
* Specify middleware that should be added to the specified resource routes.
*
* @param array|string $methods
* @param array|string $middleware
* @return $this
*/
public function middlewareFor($methods, $middleware)
{
$methods = Arr::wrap($methods);
$middleware = Arr::wrap($middleware);

if (isset($this->options['middleware'])) {
$middleware = Router::uniqueMiddleware(array_merge(
$this->options['middleware'],
$middleware
));
}

foreach ($methods as $method) {
$this->options['middleware_for'][$method] = $middleware;
}

return $this;
}

Expand All @@ -199,6 +234,25 @@ public function withoutMiddleware($middleware)
return $this;
}

/**
* Specify middleware that should be removed from the specified resource routes.
*
* @param array|string $methods
* @param array|string $middleware
* @return $this
*/
public function withoutMiddlewareFor($methods, $middleware)
{
$methods = Arr::wrap($methods);
$middleware = Arr::wrap($middleware);

foreach ($methods as $method) {
$this->options['excluded_middleware_for'][$method] = $middleware;
}

return $this;
}

/**
* Add "where" constraints to the resource routes.
*
Expand Down
30 changes: 28 additions & 2 deletions src/Illuminate/Routing/ResourceRegistrar.php
Original file line number Diff line number Diff line change
Expand Up @@ -104,8 +104,21 @@ public function register($name, $controller, array $options = [])
$resourceMethods = $this->getResourceMethods($defaults, $options);

foreach ($resourceMethods as $m) {
$optionsForMethod = $options;

if (isset($optionsForMethod['middleware_for'][$m])) {
$optionsForMethod['middleware'] = $optionsForMethod['middleware_for'][$m];
}

if (isset($optionsForMethod['excluded_middleware_for'][$m])) {
$optionsForMethod['excluded_middleware'] = Router::uniqueMiddleware(array_merge(
$optionsForMethod['excluded_middleware'] ?? [],
$optionsForMethod['excluded_middleware_for'][$m]
));
}

$route = $this->{'addResource'.ucfirst($m)}(
$name, $base, $controller, $options
$name, $base, $controller, $optionsForMethod
);

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

foreach ($resourceMethods as $m) {
$optionsForMethod = $options;

if (isset($optionsForMethod['middleware_for'][$m])) {
$optionsForMethod['middleware'] = $optionsForMethod['middleware_for'][$m];
}

if (isset($optionsForMethod['excluded_middleware_for'][$m])) {
$optionsForMethod['excluded_middleware'] = Router::uniqueMiddleware(array_merge(
$optionsForMethod['excluded_middleware'] ?? [],
$optionsForMethod['excluded_middleware_for'][$m]
));
}

$route = $this->{'addSingleton'.ucfirst($m)}(
$name, $controller, $options
$name, $controller, $optionsForMethod
);

if (isset($options['bindingFields'])) {
Expand Down
99 changes: 99 additions & 0 deletions tests/Routing/RouteRegistrarTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -844,6 +844,37 @@ public function testCanSetMiddlewareOnRegisteredResource()
$this->seeMiddleware(RouteRegistrarMiddlewareStub::class);
}

public function testCanSetMiddlewareForSpecifiedMethodsOnRegisteredResource()
{
$this->router->resource('users', RouteRegistrarControllerStub::class)
->middleware('default')
->middlewareFor('index', RouteRegistrarMiddlewareStub::class)
->middlewareFor(['create', 'store'], 'one')
->middlewareFor(['edit'], ['one', 'two']);

$this->assertEquals($this->router->getRoutes()->getByName('users.index')->gatherMiddleware(), ['default', RouteRegistrarMiddlewareStub::class]);
$this->assertEquals($this->router->getRoutes()->getByName('users.create')->gatherMiddleware(), ['default', 'one']);
$this->assertEquals($this->router->getRoutes()->getByName('users.store')->gatherMiddleware(), ['default', 'one']);
$this->assertEquals($this->router->getRoutes()->getByName('users.show')->gatherMiddleware(), ['default']);
$this->assertEquals($this->router->getRoutes()->getByName('users.edit')->gatherMiddleware(), ['default', 'one', 'two']);
$this->assertEquals($this->router->getRoutes()->getByName('users.update')->gatherMiddleware(), ['default']);
$this->assertEquals($this->router->getRoutes()->getByName('users.destroy')->gatherMiddleware(), ['default']);

$this->router->resource('users', RouteRegistrarControllerStub::class)
->middlewareFor('index', RouteRegistrarMiddlewareStub::class)
->middlewareFor(['create', 'store'], 'one')
->middlewareFor(['edit'], ['one', 'two'])
->middleware('default');

$this->assertEquals($this->router->getRoutes()->getByName('users.index')->gatherMiddleware(), [RouteRegistrarMiddlewareStub::class, 'default']);
$this->assertEquals($this->router->getRoutes()->getByName('users.create')->gatherMiddleware(), ['one', 'default']);
$this->assertEquals($this->router->getRoutes()->getByName('users.store')->gatherMiddleware(), ['one', 'default']);
$this->assertEquals($this->router->getRoutes()->getByName('users.show')->gatherMiddleware(), ['default']);
$this->assertEquals($this->router->getRoutes()->getByName('users.edit')->gatherMiddleware(), ['one', 'two', 'default']);
$this->assertEquals($this->router->getRoutes()->getByName('users.update')->gatherMiddleware(), ['default']);
$this->assertEquals($this->router->getRoutes()->getByName('users.destroy')->gatherMiddleware(), ['default']);
}

public function testResourceWithoutMiddlewareRegistration()
{
$this->router->resource('users', RouteRegistrarControllerStub::class)
Expand All @@ -856,6 +887,23 @@ public function testResourceWithoutMiddlewareRegistration()
$this->assertEquals(['one'], $this->getRoute()->excludedMiddleware());
}

public function testCanSetExcludedMiddlewareForSpecifiedMethodsOnRegisteredResource()
{
$this->router->resource('users', RouteRegistrarControllerStub::class)
->withoutMiddleware('one')
->withoutMiddlewareFor('index', 'two')
->withoutMiddlewareFor(['create', 'store'], 'three')
->withoutMiddlewareFor(['edit'], ['four', 'five']);

$this->assertEquals($this->router->getRoutes()->getByName('users.index')->excludedMiddleware(), ['one', 'two']);
$this->assertEquals($this->router->getRoutes()->getByName('users.create')->excludedMiddleware(), ['one', 'three']);
$this->assertEquals($this->router->getRoutes()->getByName('users.store')->excludedMiddleware(), ['one', 'three']);
$this->assertEquals($this->router->getRoutes()->getByName('users.show')->excludedMiddleware(), ['one']);
$this->assertEquals($this->router->getRoutes()->getByName('users.edit')->excludedMiddleware(), ['one', 'four', 'five']);
$this->assertEquals($this->router->getRoutes()->getByName('users.update')->excludedMiddleware(), ['one']);
$this->assertEquals($this->router->getRoutes()->getByName('users.destroy')->excludedMiddleware(), ['one']);
}

public function testResourceWithMiddlewareAsStringable()
{
$one = new class implements Stringable
Expand Down Expand Up @@ -1338,6 +1386,57 @@ public function testApiSingletonCanIncludeAnySingletonMethods()
$this->assertTrue($this->router->getRoutes()->hasNamedRoute('user.edit'));
}

public function testCanSetMiddlewareForSpecifiedMethodsOnRegisteredSingletonResource()
{
$this->router->singleton('users', RouteRegistrarControllerStub::class)
->creatable()
->destroyable()
->middleware('default')
->middlewareFor('show', RouteRegistrarMiddlewareStub::class)
->middlewareFor(['create', 'store'], 'one')
->middlewareFor(['edit'], ['one', 'two']);

$this->assertEquals($this->router->getRoutes()->getByName('users.create')->gatherMiddleware(), ['default', 'one']);
$this->assertEquals($this->router->getRoutes()->getByName('users.store')->gatherMiddleware(), ['default', 'one']);
$this->assertEquals($this->router->getRoutes()->getByName('users.show')->gatherMiddleware(), ['default', RouteRegistrarMiddlewareStub::class]);
$this->assertEquals($this->router->getRoutes()->getByName('users.edit')->gatherMiddleware(), ['default', 'one', 'two']);
$this->assertEquals($this->router->getRoutes()->getByName('users.update')->gatherMiddleware(), ['default']);
$this->assertEquals($this->router->getRoutes()->getByName('users.destroy')->gatherMiddleware(), ['default']);

$this->router->singleton('users', RouteRegistrarControllerStub::class)
->creatable()
->destroyable()
->middlewareFor('show', RouteRegistrarMiddlewareStub::class)
->middlewareFor(['create', 'store'], 'one')
->middlewareFor(['edit'], ['one', 'two'])
->middleware('default');

$this->assertEquals($this->router->getRoutes()->getByName('users.create')->gatherMiddleware(), ['one', 'default']);
$this->assertEquals($this->router->getRoutes()->getByName('users.store')->gatherMiddleware(), ['one', 'default']);
$this->assertEquals($this->router->getRoutes()->getByName('users.show')->gatherMiddleware(), [RouteRegistrarMiddlewareStub::class, 'default']);
$this->assertEquals($this->router->getRoutes()->getByName('users.edit')->gatherMiddleware(), ['one', 'two', 'default']);
$this->assertEquals($this->router->getRoutes()->getByName('users.update')->gatherMiddleware(), ['default']);
$this->assertEquals($this->router->getRoutes()->getByName('users.destroy')->gatherMiddleware(), ['default']);
}

public function testCanSetExcludedMiddlewareForSpecifiedMethodsOnRegisteredSingletonResource()
{
$this->router->singleton('users', RouteRegistrarControllerStub::class)
->creatable()
->destroyable()
->withoutMiddleware('one')
->withoutMiddlewareFor('show', 'two')
->withoutMiddlewareFor(['create', 'store'], 'three')
->withoutMiddlewareFor(['edit'], ['four', 'five']);

$this->assertEquals($this->router->getRoutes()->getByName('users.create')->excludedMiddleware(), ['one', 'three']);
$this->assertEquals($this->router->getRoutes()->getByName('users.store')->excludedMiddleware(), ['one', 'three']);
$this->assertEquals($this->router->getRoutes()->getByName('users.show')->excludedMiddleware(), ['one', 'two']);
$this->assertEquals($this->router->getRoutes()->getByName('users.edit')->excludedMiddleware(), ['one', 'four', 'five']);
$this->assertEquals($this->router->getRoutes()->getByName('users.update')->excludedMiddleware(), ['one']);
$this->assertEquals($this->router->getRoutes()->getByName('users.destroy')->excludedMiddleware(), ['one']);
}

/**
* Get the last route registered with the router.
*
Expand Down

0 comments on commit 7a80e6c

Please sign in to comment.