diff --git a/README.md b/README.md index f8faf3f7..a70672e4 100755 --- a/README.md +++ b/README.md @@ -167,32 +167,37 @@ class UnsetSessionMiddleware ``` ## RESTful Resources -A single line registers all standard CRUD routes: +Registers standard CRUD routes with explicit plural and singular URL patterns. +No magic pluralization — you define exactly what the URLs look like. ```php -$router->resource('api/users', UserController::class); +$router->resource('api/users', 'api/user', UserController::class); ``` This creates the following routes: -| Method | URL | Controller Method | -|--------|-----------|-------------------| -| GET | api/users | read | -| POST | api/users | create | -| PUT | api/users | update | -| PATCH | api/users | update | -| DELETE | api/users | delete | - +| Method | URL | Controller Method | Description | +|--------|-----|-------------------|-------------| +| GET | api/users | index | List all users | +| GET | api/user/:id | read | Get single user | +| POST | api/users | create | Create new user | +| PUT | api/user/:id | update | Full update user | +| PATCH | api/user/:id | update | Partial update user | +| DELETE | api/user/:id | delete | Delete user | +>The default action names are [index, read, create, update, delete]. ### Custom Method Names +You can override the default action names by passing a custom array of 5 methods: ```php -$router->resource('api/posts', PostController::class, [ - 'actionIndex', - 'actionAdd', - 'actionUpdate', - 'actionDrop' +$router->resource('api/posts', 'api/post', PostController::class, [ + 'actionIndex', // GET api/posts — list all posts + 'actionView', // GET api/post/:id — get single post + 'actionAdd', // POST api/posts — create new post + 'actionUpdate', // PUT/PATCH api/post/:id — update post + 'actionDrop' // DELETE api/post/:id — delete post ]); ``` +>The array order is fixed: [index, read, create, update, delete]. ## The set() Method — Extended Syntax Allows defining a route with multiple HTTP methods via `|`: diff --git a/src/Traits/RouterRequestMethodTrait.php b/src/Traits/RouterRequestMethodTrait.php index 8bcbde4c..077bf394 100755 --- a/src/Traits/RouterRequestMethodTrait.php +++ b/src/Traits/RouterRequestMethodTrait.php @@ -65,53 +65,73 @@ public function any(string $pattern, array|callable $target, array $middleware = } /** - * Registers a resource route, mapping standard actions to controller methods. + * Registers RESTful resource routes with explicit plural and singular URL patterns. * - * Supports common CRUD operations by default: - * - GET => read - * - POST => create - * - PUT => update - * - DELETE => delete + * No magic pluralization — you define exactly what the URLs look like. * - * Can be customized with an optional $actions array. + * Creates the following routes: + * - GET {plural} => actions[0] (index — list all) + * - GET {singular}/:id => actions[1] (read — get single) + * - POST {plural} => actions[2] (create) + * - PUT {singular}/:id => actions[3] (full update) + * - PATCH {singular}/:id => actions[3] (partial update) + * - DELETE {singular}/:id => actions[4] (delete) */ - public function resource(string $pattern, string $controller, array $actions = ['read', 'create', 'update', 'delete']): void - { - $request = $this->rudra->request(); - $server = $request->server(); - $post = $request->post(); - - $requestMethod = $server->get('REQUEST_METHOD'); - $httpMethod = $requestMethod === 'POST' && $post->has('_method') - ? strtoupper($post->get('_method')) - : $requestMethod; - - switch ($httpMethod) { - case 'GET': - $route['method'] = 'GET'; - $route['action'] = $actions[0]; // read - break; - case 'POST': - $route['method'] = 'POST'; - $route['action'] = $actions[1]; // create - break; - case 'PUT': - case 'PATCH': - $route['method'] = $httpMethod; - $route['action'] = $actions[2]; // update - break; - case 'DELETE': - $route['method'] = 'DELETE'; - $route['action'] = $actions[3]; // delete - break; - default: - return; // Unknown method — ignore - } - - $route['url'] = $pattern; - $route['controller'] = $controller; - - $this->set($route); + public function resource(string $plural, string $singular, string $controller, + array $actions = ['index', 'read', 'create', 'update', 'delete'] + ): void { + [$index, $read, $create, $update, $delete] = $actions; + + $plural = ltrim($plural, '/'); + $singular = ltrim($singular, '/'); + + // GET api/users => index + $this->set([ + 'method' => 'GET', + 'url' => $plural, + 'controller' => $controller, + 'action' => $index, + ]); + + // GET api/user/:id => read + $this->set([ + 'method' => 'GET', + 'url' => $singular . '/:id', + 'controller' => $controller, + 'action' => $read, + ]); + + // POST api/users => create + $this->set([ + 'method' => 'POST', + 'url' => $plural, + 'controller' => $controller, + 'action' => $create, + ]); + + // PUT api/user/:id => update + $this->set([ + 'method' => 'PUT', + 'url' => $singular . '/:id', + 'controller' => $controller, + 'action' => $update, + ]); + + // PATCH api/user/:id => update + $this->set([ + 'method' => 'PATCH', + 'url' => $singular . '/:id', + 'controller' => $controller, + 'action' => $update, + ]); + + // DELETE api/user/:id => delete + $this->set([ + 'method' => 'DELETE', + 'url' => $singular . '/:id', + 'controller' => $controller, + 'action' => $delete, + ]); } /** diff --git a/tests/RouterTest.php b/tests/RouterTest.php index eb40fbd0..8d18bfce 100755 --- a/tests/RouterTest.php +++ b/tests/RouterTest.php @@ -84,75 +84,60 @@ public function testAny(): void $this->assertEquals("ANY", Rudra::config()->get("actionAny")); } - protected function setRouteResourceEnvironment(string $requestMethod, string $action): void - { - $_SERVER["REQUEST_URI"] = "api/123"; + protected function setRouteResourceEnvironment( + string $requestMethod, + string $action, + string $requestUri + ): void { + $_SERVER["REQUEST_URI"] = $requestUri; $_SERVER["REQUEST_METHOD"] = $requestMethod; $this->setContainer(); - Router::resource("api/:id", MainController::class); - $this->assertEquals($action, Rudra::config()->get($action)); - } - public function testResource(): void - { - $this->setRouteResourceEnvironment("GET", "read"); - $this->setRouteResourceEnvironment("POST", "create"); - $this->setRouteResourceEnvironment("PUT", "update"); - $this->setRouteResourceEnvironment("DELETE", "delete"); - } + Router::resource( + "api/users", + "api/user", + MainController::class, + ['index', 'read', 'create', 'update', 'delete'] + ); - protected function setRouteResourcePostEnvironment(string $requestMethod, string $action): void - { - $_SERVER["REQUEST_URI"] = "api/123"; - $_SERVER["REQUEST_METHOD"] = "POST"; - $_POST["_method"] = $requestMethod; - $this->setContainer(); - Router::resource("api/:id", MainController::class); $this->assertEquals($action, Rudra::config()->get($action)); } - public function testResourcePost(): void + public function testResource(): void { - $this->setRouteResourcePostEnvironment("DELETE", "delete"); - $this->setRouteResourcePostEnvironment("PUT", "update"); - $this->setRouteResourcePostEnvironment("PATCH", "update"); + // Collection routes (plural URL, no :id) + $this->setRouteResourceEnvironment("GET", "index", "api/users"); + $this->setRouteResourceEnvironment("POST", "create", "api/users"); + + // Single-item routes (singular URL + /:id) + $this->setRouteResourceEnvironment("GET", "read", "api/user/123"); + $this->setRouteResourceEnvironment("PUT", "update", "api/user/123"); + $this->setRouteResourceEnvironment("PATCH", "update", "api/user/123"); + $this->setRouteResourceEnvironment("DELETE", "delete", "api/user/123"); } - protected function setRoutePostEnvironment(string $requestMethod, string $action): void + protected function setRouteResourcePostEnvironment(string $spoofedMethod, string $action): void { - $_SERVER["REQUEST_URI"] = "api/123"; + $_SERVER["REQUEST_URI"] = "api/user/123"; $_SERVER["REQUEST_METHOD"] = "POST"; - $_POST["_method"] = $requestMethod; + $_POST["_method"] = $spoofedMethod; $this->setContainer(); - $method = strtolower($requestMethod); + Router::resource( + "api/users", + "api/user", + MainController::class, + ['index', 'read', 'create', 'update', 'delete'] + ); - Router::resource("api/:id", MainController::class); $this->assertEquals($action, Rudra::config()->get($action)); } - public function testPostMethods(): void - { - $this->setRoutePostEnvironment("DELETE", "delete"); - $this->setRoutePostEnvironment("PUT", "update"); - $this->setRoutePostEnvironment("PATCH", "update"); - } - - public function testMiddleware() + public function testResourcePost(): void { - $_SERVER["REQUEST_URI"] = "123/456"; - $_SERVER["REQUEST_METHOD"] = "GET"; - $this->setContainer(); - - Router::get("123/:id", [MainController::class,'read'], - [ - "before" => [[Middleware::class]], - "after" => [function () { Rudra::config()->set(["after" => "after"]); }] - ] - ); - - $this->assertEquals(Middleware::class, Rudra::config()->get("middleware")); - $this->assertEquals("after", Rudra::config()->get("after")); + $this->setRouteResourcePostEnvironment("PUT", "update"); + $this->setRouteResourcePostEnvironment("PATCH", "update"); + $this->setRouteResourcePostEnvironment("DELETE", "delete"); } public function testClosure() diff --git a/tests/Stub/Controllers/MainController.php b/tests/Stub/Controllers/MainController.php index c37cdbac..c6dbea8e 100755 --- a/tests/Stub/Controllers/MainController.php +++ b/tests/Stub/Controllers/MainController.php @@ -50,6 +50,12 @@ public function actionAny() Rudra::config()->set(["actionAny" => "ANY"]); } + public function index() + { + Rudra::config()->set(["index" => "index"]); + } + + public function read($params = null) { Rudra::config()->set(["read" => "read"]);