diff --git a/src/Illuminate/Contracts/Routing/Registrar.php b/src/Illuminate/Contracts/Routing/Registrar.php index bf1c50a00927..dba7290a9d31 100644 --- a/src/Illuminate/Contracts/Routing/Registrar.php +++ b/src/Illuminate/Contracts/Routing/Registrar.php @@ -74,7 +74,7 @@ public function match($methods, $uri, $action); * @param string $name * @param string $controller * @param array $options - * @return void + * @return \Illuminate\Routing\PendingResourceRegistration */ public function resource($name, $controller, array $options = []); diff --git a/src/Illuminate/Routing/PendingResourceRegistration.php b/src/Illuminate/Routing/PendingResourceRegistration.php new file mode 100644 index 000000000000..b1f91b78a671 --- /dev/null +++ b/src/Illuminate/Routing/PendingResourceRegistration.php @@ -0,0 +1,67 @@ +registrar = $registrar; + $this->resource = $resource; + } + + /** + * Route a resource to a controller. + * + * @return void + */ + public function register() + { + $this->registrar->register( + $this->resource['name'], + $this->resource['controller'], + $this->resource['options'] + ); + } + + /** + * Set the methods the controller should apply to. + * + * @param array|string|dynamic $methods + */ + public function only($methods) + { + $this->resource['options']['only'] = is_array($methods) ? $methods : func_get_args(); + } + + /** + * Set the methods the controller should exclude. + * + * @param array|string|dynamic $methods + */ + public function except($methods) + { + $this->resource['options']['except'] = is_array($methods) ? $methods : func_get_args(); + } +} diff --git a/src/Illuminate/Routing/ResourceRegistrar.php b/src/Illuminate/Routing/ResourceRegistrar.php index 5209e9e9279a..27b7c4083900 100644 --- a/src/Illuminate/Routing/ResourceRegistrar.php +++ b/src/Illuminate/Routing/ResourceRegistrar.php @@ -62,6 +62,21 @@ public function __construct(Router $router) $this->router = $router; } + /** + * Create a pending resource registration. + * + * @param string $name + * @param string $controller + * @param array $options + * @return \Illuminate\Routing\PendingResourceRegistration + */ + public function registration($name, $controller, array $options = []) + { + return new PendingResourceRegistration( + $this, compact('name', 'controller', 'options') + ); + } + /** * Route a resource to a controller. * @@ -113,7 +128,7 @@ protected function prefixedResource($name, $controller, array $options) // are supported in the framework, but we need to know what name to use for a // place-holder on the route parameters, which should be the base resources. $callback = function ($me) use ($name, $controller, $options) { - $me->resource($name, $controller, $options); + $me->resource($name, $controller, $options)->register(); }; return $this->router->group(compact('prefix'), $callback); diff --git a/src/Illuminate/Routing/RouteRegistrar.php b/src/Illuminate/Routing/RouteRegistrar.php index 113045dae1e2..52bb65fa3579 100644 --- a/src/Illuminate/Routing/RouteRegistrar.php +++ b/src/Illuminate/Routing/RouteRegistrar.php @@ -86,11 +86,11 @@ public function attribute($key, $value) * @param string $name * @param string $controller * @param array $options - * @return void + * @return \Illuminate\Routing\PendingResourceRegistration */ public function resource($name, $controller, array $options = []) { - $this->router->resource($name, $controller, $this->attributes + $options); + return $this->router->resource($name, $controller, $this->attributes + $options); } /** diff --git a/src/Illuminate/Routing/Router.php b/src/Illuminate/Routing/Router.php index f70b3611beb4..5497aa89c3f9 100644 --- a/src/Illuminate/Routing/Router.php +++ b/src/Illuminate/Routing/Router.php @@ -94,6 +94,13 @@ class Router implements RegistrarContract, BindingRegistrar */ protected $patterns = []; + /** + * The resources not yet registered. + * + * @var array + */ + protected $pendingResources = []; + /** * The route group attribute stack. * @@ -240,7 +247,7 @@ public function resources(array $resources) * @param string $name * @param string $controller * @param array $options - * @return void + * @return \Illuminate\Routing\PendingResourceRegistration */ public function resource($name, $controller, array $options = []) { @@ -250,7 +257,7 @@ public function resource($name, $controller, array $options = []) $registrar = new ResourceRegistrar($this); } - $registrar->register($name, $controller, $options); + return $this->pendingResources[] = $registrar->registration($name, $controller, $options); } /** @@ -530,7 +537,7 @@ public function dispatchToRoute(Request $request) */ protected function findRoute($request) { - $this->current = $route = $this->routes->match($request); + $this->current = $route = $this->getRoutes()->match($request); $this->container->instance(Route::class, $route); @@ -903,7 +910,7 @@ public function current() */ public function has($name) { - return $this->routes->hasNamedRoute($name); + return $this->getRoutes()->hasNamedRoute($name); } /** @@ -1049,9 +1056,25 @@ public function resourceVerbs(array $verbs = []) */ public function getRoutes() { + $this->registerPendingResources(); + return $this->routes; } + /** + * Register all pending resources. + * + * @return void + */ + public function registerPendingResources() + { + foreach ($this->pendingResources as $key => $resource) { + $resource->register(); + } + + $this->pendingResources = []; + } + /** * Set the route collection instance. * diff --git a/tests/Routing/RouteRegistrarTest.php b/tests/Routing/RouteRegistrarTest.php index 187b4f04beab..9cb61af09739 100644 --- a/tests/Routing/RouteRegistrarTest.php +++ b/tests/Routing/RouteRegistrarTest.php @@ -187,6 +187,44 @@ public function testCanRegisterResource() $this->seeMiddleware('resource-middleware'); } + public function testCanLimitMethodsOnRegisteredResource() + { + $this->router->resource('users', 'Illuminate\Tests\Routing\RouteRegistrarControllerStub') + ->only('index', 'show', 'destroy'); + + $this->assertCount(3, $this->router->getRoutes()); + + $this->assertTrue($this->router->getRoutes()->hasNamedRoute('users.index')); + $this->assertTrue($this->router->getRoutes()->hasNamedRoute('users.show')); + $this->assertTrue($this->router->getRoutes()->hasNamedRoute('users.destroy')); + } + + public function testCanExcludeMethodsOnRegisteredResource() + { + $this->router->resource('users', 'Illuminate\Tests\Routing\RouteRegistrarControllerStub') + ->except(['index', 'create', 'store', 'show', 'edit']); + + $this->assertCount(2, $this->router->getRoutes()); + + $this->assertTrue($this->router->getRoutes()->hasNamedRoute('users.update')); + $this->assertTrue($this->router->getRoutes()->hasNamedRoute('users.destroy')); + } + + public function testCanNotLimitMethodsAfterRegistered() + { + $resource = $this->router->resource('users', 'Illuminate\Tests\Routing\RouteRegistrarControllerStub'); + + $this->router->getRoutes(); + + // pending resources have now been registered and + // can not be limited to specific methods anymore + + $resource->only('index', 'show'); + $resource->except('index', 'show'); + + $this->assertCount(7, $this->router->getRoutes()); + } + public function testCanSetRouteName() { $this->router->as('users.index')->get('users', function () {