--- name: Wheels Routing Generator description: Generate RESTful routes, nested routes, and custom routing patterns for Wheels applications. Use when defining URL structure, creating RESTful resources, or implementing custom route patterns. Ensures proper HTTP verb mapping and route constraints. --- # Wheels Routing Generator ## When to Use This Skill Activate automatically when: - User wants to create routes - User mentions: routes, routing, URL structure, RESTful - User needs nested resources - User wants custom route patterns - User asks about URL mapping or route constraints ## Routes Configuration File **Location:** `/config/routes.cfm` ```cfm // Basic structure mapper() // Your routes here .end(); ``` ## Basic Route Patterns ### Simple Routes ```cfm mapper() // GET request to /about -> Pages.about() .get(name="about", pattern="about", to="pages##about") // POST request to /contact -> Pages.contact() .post(name="contact", pattern="contact", to="pages##contact") // Multiple HTTP verbs .match(name="search", pattern="search", to="posts##search", methods="GET,POST") // Route with parameter .get(name="post", pattern="posts/[key]", to="posts##show") // Root route (home page) .root(to="pages##index") .end(); ``` ### RESTful Resources ```cfm mapper() // Complete RESTful resource // Creates 7 routes: index, new, create, show, edit, update, delete .resources("posts") // Limit to specific actions .resources(name="comments", only="index,show") .resources(name="photos", except="delete") .end(); ``` **Generated Routes for `.resources("posts")`:** | HTTP Verb | Path | Action | Purpose | |-----------|-------------------|---------|----------------------| | GET | /posts | index | List all posts | | GET | /posts/new | new | Show create form | | POST | /posts | create | Create new post | | GET | /posts/[key] | show | Show single post | | GET | /posts/[key]/edit | edit | Show edit form | | PATCH/PUT | /posts/[key] | update | Update post | | DELETE | /posts/[key] | delete | Delete post | ## Nested Resources ### Parent-Child Resources ```cfm mapper() // Posts with nested comments .resources("posts") .resources(name="comments", nested=true) // /posts/1/comments - shows comments for post 1 // /posts/1/comments/2 - shows comment 2 for post 1 .end(); ``` ### Multiple Level Nesting ```cfm mapper() // Blog -> Post -> Comments .resources("blogs") .resources(name="posts", nested=true) .resources(name="comments", nested=true) // /blogs/1/posts/2/comments/3 .end(); ``` ### Shallow Nesting (Recommended) ```cfm mapper() // Nested for creation only .resources("posts") .resources(name="comments", nested=true, only="new,create") // Standalone for viewing/editing .resources("comments", except="new,create") // Result: // POST /posts/1/comments (create with parent context) // GET /comments/1 (view without deep nesting) // PATCH /comments/1 (update without deep nesting) .end(); ``` ## Custom Route Patterns ### Named Routes with Parameters ```cfm mapper() // Single parameter .get(name="userProfile", pattern="users/[username]", to="users##show") // Multiple parameters .get( name="blogPost", pattern="[year]/[month]/[slug]", to="posts##show" ) // Optional parameters .get( name="search", pattern="search/[[category]]/[[tag]]", to="search##index" ) .end(); ``` ### Wildcard Routes ```cfm mapper() // Catch-all pattern (use sparingly!) .get( name="pages", pattern="pages/[*path]", to="pages##show" ) // Matches: /pages/about, /pages/help/faq, etc. .end(); ``` ### Route Constraints ```cfm mapper() // Numeric constraint .get( name="post", pattern="posts/[key]", to="posts##show", constraints={key="[0-9]+"} ) // Alpha constraint .get( name="tag", pattern="tags/[slug]", to="tags##show", constraints={slug="[a-z-]+"} ) // Date constraint .get( name="archive", pattern="[year]/[month]", to="posts##archive", constraints={ year="[0-9]{4}", month="[0-9]{2}" } ) .end(); ``` ## Namespace and Scoped Routes ### Controller Namespace ```cfm mapper() // Admin section .namespace("admin") .resources("users") .resources("posts") .resources("settings") .endNamespace() // Maps to: // /admin/users -> admin.Users.index() // /admin/posts/1 -> admin.Posts.show(key=1) .end(); ``` ### Path Scoping ```cfm mapper() // Scope multiple routes under a path .scope(path="api/v1", module="api.v1") .resources("posts") .resources("comments") .endScope() // Maps to: // /api/v1/posts -> api.v1.Posts.index() // /api/v1/comments -> api.v1.Comments.index() .end(); ``` ## API Versioning Routes ```cfm mapper() // API v1 .scope(path="api/v1", module="api.v1") .resources("posts") .resources("users") .endScope() // API v2 .scope(path="api/v2", module="api.v2") .resources("posts") .resources("users") .endScope() // Latest (redirects to current version) .get(name="apiLatest", pattern="api/[*path]", to="api##redirectToLatest") .end(); ``` ## RESTful Member and Collection Routes ### Member Routes (Single Resource) ```cfm mapper() .resources("posts") // Custom actions on single post .member(name="publish", to="posts##publish", method="patch") .member(name="archive", to="posts##archive", method="post") .endResources() // Maps to: // PATCH /posts/1/publish // POST /posts/1/archive .end(); ``` ### Collection Routes (All Resources) ```cfm mapper() .resources("posts") // Custom actions on collection .collection(name="search", to="posts##search", method="get") .collection(name="export", to="posts##export", method="get") .endResources() // Maps to: // GET /posts/search // GET /posts/export .end(); ``` ## Route Helpers in Controllers ### Generating URLs ```cfm // In controllers/views urlFor(route="post", key=1) // -> /posts/1 urlFor(route="editPost", key=1) // -> /posts/1/edit urlFor(route="posts") // -> /posts // With query parameters urlFor(route="posts", params={tag="rails", page=2}) // -> /posts?tag=rails&page=2 // Absolute URLs urlFor(route="post", key=1, onlyPath=false) // -> http://yoursite.com/posts/1 ``` ### Redirecting ```cfm // Redirect to named route redirectTo(route="posts") redirectTo(route="post", key=1) // Redirect with flash redirectTo(route="posts", success="Post created!") ``` ### Link Helpers in Views ```cfm // Link to route #linkTo(route="posts", text="All Posts")# // Link with parameters #linkTo(route="post", key=postId, text=post.title)# // Link with HTML attributes #linkTo(route="post", key=1, text="Read More", class="btn btn-primary")# ``` ## Form Routes ```cfm #startFormTag(route="posts", method="post")# #endFormTag()# #startFormTag(route="post", key=post.id, method="patch")# #endFormTag()# #linkTo( route="post", key=post.id, method="delete", text="Delete", confirm="Are you sure?" )# ``` ## Advanced Routing Patterns ### Subdomain Routes ```cfm mapper() // Admin subdomain .scope(subdomain="admin") .resources("users") .resources("settings") .endScope() // API subdomain .scope(subdomain="api") .resources("posts") .endScope() // Wildcard subdomain .scope(subdomain="[account]") .root(to="accounts##dashboard") .resources("projects") .endScope() .end(); ``` ### Redirect Routes ```cfm mapper() // Permanent redirect .redirect(from="old-blog", to="posts", statusCode=301) // Redirect to external URL .redirect( from="docs", to="https://docs.yoursite.com", statusCode=302 ) .end(); ``` ### Route Concerns (Reusable Route Sets) ```cfm mapper() // Define concern .concern(name="commentable") .resources(name="comments", nested=true) .endConcern() // Use concern .resources("posts") .concerns("commentable") .endResources() .resources("photos") .concerns("commentable") .endResources() // Both posts and photos now have comments routes .end(); ``` ## Testing Routes ### View All Routes ```cfm // Create a test action function testRoutes() { routes = application.wheels.routes; writeDump(routes); abort; } ``` ### Check Route Mapping ```cfm // Test if route exists if (structKeyExists(application.wheels.namedRoutes, "post")) { // Route exists } // Get route pattern pattern = application.wheels.namedRoutes.post.pattern; ``` ## Route Best Practices ### ✅ DO: - Use RESTful conventions (resources) - Keep nesting shallow (max 2 levels) - Use named routes for flexibility - Add route constraints for validation - Group related routes with scopes - Use proper HTTP verbs - Follow REST conventions consistently ### ❌ DON'T: - Over-nest resources (avoid `/a/b/c/d/e`) - Use catch-all routes carelessly - Hardcode URLs in code (use urlFor) - Mix REST and non-REST patterns - Create ambiguous routes - Forget route constraints on IDs - Use GET for destructive actions ## Common Route Patterns ### Blog Application ```cfm mapper() .root(to="posts##index") // Public routes .get(name="about", pattern="about", to="pages##about") .get(name="contact", pattern="contact", to="pages##contact") .post(name="contactSubmit", pattern="contact", to="pages##sendContact") // Blog routes .resources("posts", only="index,show") .get(name="archive", pattern="archive/[year]/[month]", to="posts##archive") .get(name="tag", pattern="tags/[slug]", to="posts##byTag") // Admin section .namespace("admin") .resources("posts") .resources("comments") .resources("tags") .endNamespace() // Authentication .get(name="login", pattern="login", to="sessions##new") .post(name="sessionCreate", pattern="login", to="sessions##create") .delete(name="logout", pattern="logout", to="sessions##delete") .end(); ``` ### E-commerce Application ```cfm mapper() .root(to="products##index") // Products .resources("products", only="index,show") .get(name="category", pattern="categories/[slug]", to="products##category") // Shopping cart .get(name="cart", pattern="cart", to="cart##show") .post(name="addToCart", pattern="cart/add", to="cart##add") .delete(name="removeFromCart", pattern="cart/remove/[key]", to="cart##remove") // Checkout .get(name="checkout", pattern="checkout", to="checkout##index") .post(name="processCheckout", pattern="checkout", to="checkout##process") // User account .get(name="account", pattern="account", to="users##show") .resources("orders", only="index,show") // Admin .namespace("admin") .resources("products") .resources("orders") .resources("customers") .endNamespace() .end(); ``` ### API Routes ```cfm mapper() // API v1 .scope(path="api/v1", module="api.v1") // Resources with token auth .resources("posts") .resources("comments") .resources("users", except="new,edit") // Custom endpoints .post(name="apiLogin", pattern="auth/login", to="auth##login") .post(name="apiLogout", pattern="auth/logout", to="auth##logout") .get(name="apiProfile", pattern="me", to="users##profile") .endScope() .end(); ``` ## Debugging Routes ### List All Routes Add to a controller: ```cfm function listRoutes() { routes = []; for (local.key in application.wheels.namedRoutes) { local.route = application.wheels.namedRoutes[local.key]; arrayAppend(routes, { name = local.key, pattern = local.route.pattern, controller = local.route.controller, action = local.route.action, methods = local.route.methods }); } writeDump(routes); abort; } ``` ### Test Route Generation ```cfm // Test in view/controller ``` ## Related Skills - **wheels-controller-generator**: Create controllers for routes - **wheels-api-generator**: Create API routes - **wheels-view-generator**: Create views for routes - **wheels-test-generator**: Test routing --- **Generated by:** Wheels Routing Generator Skill v1.0