You are viewing documentation for v1.0.x. Change

Using Routes

The convention for URLs in Wheels works for most situations and helps to promote an easy-to-maintain code base. With routes, you have the flexibility to break this convention when needed.

The Convention for URLs

To write clear MVC applications with ColdFusion on Wheels, we recommend sticking to conventions as much as possible. As you may already know, the convention for URLs is as follows:

http://www.domain.com/news/story/5

With this convention, the URL above tells Wheels to invoke the story action in the news controller. It also passes a parameter to the action called key, with a value of 5.

Creating Your Own Routes

But let's say that you wanted a simpler URL for your site's user profiles. What if you wanted to have a profile action in a controller called user with this URL?

http://www.domain.com/user/johndoe

Fear not, this is possible in Wheels.

Adding a New Route

Routes are configured in the config/routes.cfm file. This is where we'll add our new user profile route.

Routes are added to Wheels using the addRoute() function. Here is how we would set up our new route using addRoute():

<cfset
addRoute(
name="userProfile", pattern="user/[username]",
controller="user", action="profile"
)
>

This call to addRoute() instructs Wheels to create a route named userProfile that passes a username parameter to the profile action in the user controller. This route will be invoked by any URL that starts with a top level folder of user. In most cases, a pattern in a route should begin with a unique top level folder.

As you can see, any new parameters that you want to introduce to a new route should be surrounded by square brackets [].

With this, you can create URL patterns with any level of complexity that you wish.

Using controller and action Within Your Route

As you saw above, we specifically told Wheels which controller and action that the userProfile route should call. You also have the option of making the controller and action calls dynamic by including them in the pattern instead. (This is actually how Wheels sets up the default routes internally.)

You still have to pass in the controller and action arguments to the addRoute() function, but if [controller] and [action] also exist in the pattern, these arguments will only serve to tell Wheels what the default controller and action should be when they are not passed in through the URL.

Consider this line of code:

<cfset
addRoute(
name="adminUser", pattern="admin/user/[action]",
controller="adminUser", action="index"
)
>

With this pattern, a URL that begins with admin/user/ will always call the adminUser controller. But what action to call on that controller is determined dynamically by the URL.

But because we have [action] within the pattern, we don't always necessarily need for the route to point to controllers/AdminUser.cfc's index() method. The action="index" parameter instructs Wheels to call the index action when one is not otherwise specified in the URL.

So now this route pattern will, for example, match for these URLs:

URL Controller Action
http://www.domain.com/admin/user/edit adminUser edit
http://www.domain.com/admin/user/add adminUser add
http://www.domain.com/admin/user/delete adminUser delete

Although probably less useful, the same concept can be applied to the controller variable as well.

Linking to Your New Route

Now if you wanted to create a link to that user profile action we discussed earlier in the chapter, you would use Wheels's linkTo() function like so:

#linkTo(route="userProfile", username="johndoe")#

As you can see, linkTo() accepts a route argument, which changes the function's expectations on which other arguments are passed. Because our userProfile route expects a username parameter, we would need to pass that.

You can read more about creating links in the chapter called Linking Pages.

Order of Precedence

With the potential of your application requiring many custom routes, you may wonder which order that Wheels considers these new routes. The answer is that Wheels gives precedence to the first listed custom route in your config/routes.cfm file.

Wheels will look through each custom route in order to see if there is a match. If not, it defaults to the default route mentioned at the beginning of this chapter under "The Convention for URLs".

Example of Precedence

Let's pretend that our config/routes.cfm file looks like this:

<cfset addRoute(name="newsAdmin", pattern="admin/news/[action]"), controller="newsAdmin")>
<cfset addRoute(name="searchAdmin", pattern="admin/search/[action]", controller="searchAdmin")>
<cfset addRoute(name="adminRoot", pattern="admin/[action]", controller="admin")>

Wheels would make sure that the URL didn't begin with admin/news or admin/search before calling the third route listed, adminRoot.

If the URL didn't begin with admin at all, Wheels would use its internal default route, matching the usual pattern of [controller]/[action]/[key].

Making a Catch-All Route

Sometimes you need a catch-all route in Wheels to support highly dynamic websites (like a content management system, for example), where all requests that are not matched by an existing route get passed to a controller/action that can deal with it.

Let's say you want to have both http://localhost/welcome-to-the-site and http://localhost/terms-of-use handled by the same controller and action. Here's what you can do to achieve this.

First, add a new route to config/routes.cfm that catches all pages like this:

<cfset addRoute(name="catchAll", pattern="[title]", controller="page", action="index")>

Now when you type in http://localhost/welcome-to-the-site, this route will be triggered and the index action will be called on the page controller with params.title set to welcome-to-the-site.

The problem with this is that this will break any of your normal controllers though, so you'll need to add them specifically before this route. (Remember the order of precedence explained above.)

You'll end up with a config/routes.cfm file looking something like this:

<cfset addRoute(name="main", pattern="main/[action]", controller="main")>
<cfset addRoute(name="admin", pattern="admin/[action]", controller="admin")>
<cfset addRoute(name="catchAll", pattern="[title]", controller="page", action="index")>
<cfset addRoute(name="home", pattern="", controller="main", action="index")>

main and admin are your normal controllers. By adding them to the top of the routes file, Wheels looks for them first.

^ Top
Table of Contents

Comments

Read and submit questions, clarifications, and corrections about this chapter.

[Add Comment]

  1. James Gibson's Gravatar James Gibson says:

    Have you guys thought about implementing restful routing within the framework?

    - James

  2. Chris Peters's Gravatar Chris Peters says:

    We've thought about doing things like RESTful routing in future versions but really want to have the basics rock solid before hitting version 1.0.

    Future versions will feature a plugins capability. This will create the opportunity for the community to contribute plugins for stuff like that. (Hint hint. ;))

  3. stylo~'s Gravatar stylo~ says:

    >>http://www.domain.com/news/story/5
    >>With this convention, the URL above tells Wheels to invoke the story action in the news controller. It also passes a parameter to the action called id, with a value of 5.

    Actually, it passes the parameter "key", not "id". It will only map to "id" in a findbykey() if id is the primary key. Correct?

  4. Sin's Gravatar Sin says:

    hi,
    what is restful routing? do you guys like to add boolean valued attributes to addroute(), like 'required-login' and 'role-is-granted'?

  5. YES RAJ's Gravatar YES RAJ says:

    Your presentation is very easy to understand. The core of the framework is light enough to give
    more flexibility to users to customize the implementation of  the framework.

    Regards,
    Yes Raj

  6. dcolumbus's Gravatar dcolumbus says:

    I'm trying to get the Catch-All to work properly:

    <cfset addRoute(name="updateUser", pattern="user/update/[action]", controller="User", action="update")>
    <cfset addRoute(name="userRoot", pattern="user/[action]", controller="User", action="index")>

    <cfset addRoute(name="usersAdmin", pattern="admin/users/[action]", controller="Admin", action="users")>
    <cfset addRoute(name="adminRoot", pattern="admin/[action]", controller="Admin", action="index")>

    <cfset addRoute(name="catchAll", pattern="[company]", controller="Front", action="index")>

    <cfset addRoute(name="home", pattern="", controller="wheels", action="wheels")>

    In this case, the "user" and "admin" urls should be pulled up... but, the catch-all grabs them every time. So, this isn't working for me the way it is supposed to be.

  7. Vikram's Gravatar Vikram says:

    Hi

    I am just statring on CFWheels so it may be stupid question.

    Is the latest version of CFWheels 1.1.3 implementing Restful routing?

    Somebody above has asked the same question in 2008, at that time it was not implemented.

    Thanks
    Vikram

  8. Octavian's Gravatar Octavian says:

    I guess that creating multi language sites can be done by routing.

    Can you please provide an example in this direction? Also, it would be most important for a framework to natively support multi language websites. I was amazed when I found out that Wheels does not.

  9. Philip's Gravatar Philip says:

    I would like to create routes in combination of three things city/category/store like this
    search/city/[cityname]/[storename]/[categoryname]
    search/city/[cityname]/[storename]
    search/city/[cityname]/[categoryname]

    is it possible?

    Thanks
    Philip

  10. Tom K's Gravatar Tom K says:

    @Phillip

    In case you've not found an answer yet, yes: (although in your example, storename would clash with categoryname for the latter two entries

    <cfset addRoute(name="citySearch", pattern="search/[action]/[city]/[category]/[store]", controller="Search", action="citySearch")>
    <cfset addRoute(name="citySearch", pattern="search/[action]/[city]/[category]/", controller="Search", action="citySearch")>
    <cfset addRoute(name="citySearch", pattern="search/[action]/[city]/", controller="Search", action="citySearch")>

    So a URL of search/bycity/boston/food/joes-store would map to the params in the first line.

  11. Joel's Gravatar Joel says:

    Support for subdomains in route patters would be really useful. Say if you wanted to pass a variable to all actions in your application, like [city].store.com. I know there is a plugin for older versions of wheels that provides this functionality. It would nice to see it added natively to present and future versions.

  12. Joel's Gravatar Joel says:

    How would one change the global behaviour of wheels routes. It would be nice to tell wheels that instead of /controller/action/key, the default patter is /[city]/controller/action/key. Is this possible?

Add Comment