created in the spirit of Ruby on Rails.
How Wheels handles an incoming request.
ColdFusion on Wheels is quite simple when it comes to figuring out how incoming requests map to code in your application. Let's look at a URL for an e-commerce website and dissect it a little. Before we do that though a quick introduction to URLs in Wheels is in order.
URLs in Wheels generally look something like this: http://localhost/index.cfm/shop/products. In this specific case shop is the name of the controller to call and products is the name of the action to call on that controller.
Unless you're familiar with the Model-View-Controller pattern you're probably wondering what controllers and actions are.
Put very simply a controller takes an incoming request and, based on the parameters in the URL, decides what (if any) data to get from the model (which in most cases means your database) and which view (which in most cases means a CFML file producing HTML output) to display to the user.
An action is the entire process of executing code in the controller, including a view file and rendering the result to the browser. As you will see in the example below an action usually maps directly to one specific function with the same name in the controller file.
Mapping an incoming URL to code is only one side of the equation, you will also need a way to create these URLs (unless you want to hard-code them). This is done through a variety of different functions like
linkTo() (for creating links),
startFormTag() (for creating forms) and
redirectTo() (for redirecting users) to name a few. Internally all of these functions use the same code to create the URL though, namely the
URLFor() function. The
URLFor() function accepts a controller and an action argument which are what you will use most of the time. It has a lot of other arguments though and does some neat stuff (like defaulting to the current controller when you don't specifically pass one in) so check out the APi for this function for all the details.
By the way, by making clever use of URL rewriting in Apache or IIS you can completely get rid of the index.cfm part of the URL so that http://localhost/index.cfm/shop/products becomes http://localhost/shop/products. You can read more about this in the URL Rewriting chapter. For the remainder of this chapter we'll type out the URLs in this shorter and prettier way.
Let's look a little closer at what happens when Wheels receives this incoming request now. First, it will create an instance of the shop controller (controller/shop.cfc) and call the function inside it named products.
Let's show how the code for the products function could look to make it more clear what goes on:
<cfcomponent extends="controller">
<cffunction name="products">
<cfset renderPage(controller="shop", action="products")>
</cffunction>
</cfcomponent>
The only thing this does is call a view page to render using the
renderPage() function. The
renderPage() function is available to you since the shop controller extends the main Wheels controller so don't forget to include that extends attribute in the cfcomponent tag as you build your controllers since otherwise you won't be able to do much.
So, how does
renderPage() work? Well, it accepts the arguments controller and action among others and based on these it will try to include a view file, in our case this is the view\shop\products.cfm file.
You can read the chapter about Rendering Pages for more information about this function.
Because Wheels favors convention over configuration we can actually remove a lot of the code in the example above and it will still work because Wheels will just guess what your intention is. Let's have a quick look at eactly what code can be removed and why.
The first thing Wheels assumes is that if you call
renderPage() without arguments you want to include the view page for the current controller and action. Therefore the code above can be changed to:
<cfcomponent extends="controller">
<cffunction name="products">
<cfset renderPage()>
</cffunction>
</cfcomponent>
... and it will still work just fine.
Does Wheels assume anything else? Sure it does. You can actually remove the entire
renderPage() call because Wheels will assume that you always want to call a view page when the processing in the controller is done and simply call it for you behind the scenes. This leaves you with this code:
<cfcomponent extends="controller">
<cffunction name="products">
</cffunction>
</cfcomponent>
That looks rather silly, a products function with no code what so ever. What do you think will happen if you just remove that entire function leaving you with this code?
<cfcomponent extends="controller">
</cfcomponent>
... if you guessed that Wheels will just assume you don't need any code for the products action and just want the view rendered directly, then you are correct. This is quite useful when you're just adding simple pages to a website and you don't need the controller and model to be involved at all. For example you can create a file named about.cfm in the view/home folder and access it at http://localhost/home/about without having to create a specific action for it in the controller.
This also highlights the fact that Wheels is a very easy framework to get started in since you can basically program just as you normally would by creating simple pages like this and then gradually "Wheelsify" your code as you learn the framework.
Besides making sure the correct code is executed based on format of the incoming request Wheels also does something else to simplify things for you a little. It combines the URL and form scope into one. This is something that most ColdFusion frameworks do as well. In Wheels it is done in the params struct. The params struct is available to you in the controller and view files but not in the model files (since it's considered a best practice to not mix your request handling with your business logic). Besides the form and URL scope variables the params struct also contains the current controller and action name for easy reference.
To make this concept easier to grasp, imagine a login form on your website that submits to http://localhost/account/login?sendto=dashboard with the variables username and password present in the form. Then your params struct will look like this:
| Name | Value |
|---|---|
| params.controller | account |
| params.action | login |
| params.sendto | dashboard |
| params.username | joe |
| params.password | 1234 |
Now instead of accessing the variables as URL.sendto, form.username etc you can just use the params struct for all of them instead. This concept becomes even more useful once we start getting into creating forms specifically meant for accessing object properties but let's save the details of all that for the Form Helpers and Showing Errors chapter.
For more advanced URL to code mappings you can use a concept called "routing". It allows you to fully customize every URL in your application. You can read more about this in the chapter called Using Routes.