Install CFWheels and get a local development server running
By far the quickest way to get started with CFWheels is via CommandBox. CommandBox brings a whole host of command line capabilities to the CFML developer. It allows you to write scripts that can be executed at the command line written entirely in CFML. It allows you to start a CFML server from any directory on your machine and wire up the code in that directory as the web root of the server. What's more is, those servers can be either Lucee servers or Adobe ColdFusion servers. You can even specify what version of each server to launch. Lastly, CommandBox is a package manager for CFML. That means you can take some CFML code and package it up into a module, host it on ForgeBox.io, and make it available to other CFML developers. In fact we make extensive use of these capabilities to distribute CFWheels plugins and templates. More on that later.
One module that we have created is a module that extends CommandBox itself with commands and features specific to the CFWheels framework. The CFWheels CLI module for CommandBox is modeled after the Ruby on Rails CLI module and gives similar capabilities to the CFWheels developer.
The first step is to get CommandBox downloaded and running. CommandBox is available for Windows, Mac & Linux, and can be installed manually or using one of the respective package managers for each OS. You can use Chocolatey on Windows, Homebrew on MacOS, or Yum/Apt on Linux depending on your flavor of Linux. Please follow the instructions on how to install CommandBox on your particular operating system. At the end of the installation process you want to make sure the box command is part of your system path so you can call the command from any directory on your system.
Once installed, you can either double-click on the box executable which opens the CommandBox shell window, or run box from a CMD window in Windows, Terminal window in MacOS, or shell prompt on a Linux server. Sometimes you only want to call a single CommandBox command and don't need to launch a whole CommandBox shell window to do that, for these instances you can call the CommandBox command directly from your default system terminal window by prefixing the command with the box prefix.
So to run the CommandBox version command you could run box version from the shell or you could launch the CommandBox shell and run version inside it.
box version
version
This is a good concept to grasp, cause depending on your workflow, you may find it easier to do one versus the other. Most of the commands you will see in these CLI guides will assume that you are entering the command in the actual CommandBox shell so the box prefix is left off.
Okay, now that we have CommandBox installed, let's add the CFWheels CLI module.
install cfwheels-cli
Installing this module will add a number of commands to your default CommandBox installation. All of these commands are prefixed by the wheels name space. There are commands to create a brand new CFWheels application or scaffold out sections of your application. We'll see some of these commands in action momentarily.
Now that we have CommandBox installed and extended it with the CFWheels CLI module, let's start our first CFWheels app from the command line. We'll look at the simplest method for creating a CFWheels app and starting our development server.
wheels generate app myApp server start
A few minutes after submitting the above commands a new browser window should open up and display the default CFWheels congratulations screen.
So what just happened? Since we only passed the application name myApp to the wheels generate app command, it used default values for most of its parameters and downloaded our Base template (cfwheels-base-template) from ForgeBox.io, then downloaded the framework core files (cfwheels) from ForgeBox.io and placed it in the wheels directory, then configured the application name and reload password, and started a Lucee server on a random port.
This getting started guide has taken you from the very beginning and gotten you to the point where you can go into any empty directory on your local development machine and start a CFWheels project by issuing a couple of CLI commands. In later guides we'll explore these options further and see what else the CLI can do for us.


Using CFWheels to develop web applications with AJAX features is a breeze. You have several options and tools at your disposal, which we'll cover in this chapter.
CFWheels was designed to be as lightweight as possible, so this keeps your options fairly open for developing AJAX features into your application.
While there are several flavors of JavaScript libraries out there with AJAX support, we will be using the jQuery framework in this tutorial. Let's assume that you are fairly familiar with the basics of jQuery and know how to set it up.
For this tutorial, let's create the simplest example of all: a link that will render a message back to the user without refreshing the page.
In this example, we'll wire up some simple JavaScript code that calls a CFWheels action asynchronously. All of this will be done with basic jQuery code and built-in CFWheels functionality.
First, let's make sure we've got an appropriate route setup. It might be you're still using the default wildcard() route which will create some default GET routes for the controller/action pattern, but we'll add a new route here just for practice:
mapper()
.get(name="sayHello", to="say##hello")
.end()Then, let's create a link to a controller's action in a view file, like so:
<cfoutput>
<!--- View code --->
<h1></h1>
<p></p>
#linkTo(text="Alert me!", route="sayHello", id="alert-button")#
</cfoutput>That piece of code by itself will work just like you expect it to. When you click the link, you will load the hello action inside the say controller.
But let's make it into an asynchronous request. Add this JavaScript (either on the page inside script tags or in a separate .js file included via javaScriptIncludeTag() ):
(function($) {
// Listen to the "click" event of the "alert-button" link and make an AJAX request
$("#alert-button").on("click", function(event) {
$.ajax({
type: "GET",
// References "/say/hello?format=json"
url: $(this).attr("href") + "?format=json",
dataType: "json",
success: function(response) {
$("h1").html(response.message);
$("p").html(response.time);
}
});
event.preventDefault();
});
})(jQuery);With that code, we are listening to the click event of the hyperlink, which will make an asynchronous request to the hello action in the say controller. Additionally, the JavaScript call is passing a URL parameter called format set to json.
Note that the success block inserts keys from the response into the empty h1 and p blocks in the calling view. (You may have been wondering about those when you saw the first example. Mystery solved.)
The last thing that we need to do is implement the say/hello action. Note that the request expects a dataType of JSON. By default, CFWheels controllers only generate HTML responses, but there is an easy way to generate JSON instead using CFWheels's provides() and renderWith() functions:
component extends="Controller" {
function config() {
provides("html,json");
}
function hello() {
// Prepare the message for the user.
greeting = {};
greeting["message"] = "Hi there";
greeting["time"] = Now();
// Respond to all requests with `renderWith()`.
renderWith(greeting);
}
}In this controller's config() method, we use the provides() function to indicate that we want all actions in the controller to be able to respond with the data in HTML or JSON formats. Note that the client calling the action can request the type by passing a URL parameter named format or by sending the format in the request header.
The call to renderWith() in the hello action takes care of the translation to the requested format. Our JavaScript is requesting JSON, so Wheels will format the greeting struct as JSON automatically and send it back to the client. If the client requested HTML or the default of none, Wheels will process and serve the view template at views/say/hello.cfm. For more information about provides() and renderWith(), reference the chapter on Responding with Multiple Formats.
Lastly, notice the lines where we're setting greeting["message"] and greeting["time"]. Due to the case-insensitive nature of ColdFusion, we recommend setting variables to be consumed by JavaScript using bracket notation like that. If you do not use that notation (i.e., greetings.message and greetings.time instead), your JavaScript will need to reference those keys from the JSON as MESSAGE and TIME (all caps). Unless you like turning caps lock on and off, you can see how that would get annoying after some time.
Assuming you already included jQuery in your application and you followed the code examples above, you now have a simple AJAX-powered web application built on Wheels. After clicking that Alert me! link, your say controller will respond back to you the serialized message via AJAX. jQuery will parse the JSON object and populate the h1 and pwith the appropriate data.
That is it! Hopefully now you have a clearer picture on how to create AJAX-based features for your web applications.
With CommandBox, we don't need to have Lucee or Adobe ColdFusion installed locally. With a simple command, we can make CommandBox go and get the CFML engine we've requested, and quickly create a server running on Undertow. Make sure you're in the root of your website, and then run:
server start
The server will then start on a random port on 127.0.0.1 and will create a server.json file in your webroot. We can add various options to server.json to customize our server. Here's an example:
In this example, I've set the server name to myApp, meaning I can now start the server from any directory by simply calling start myApp. I've also specified a specific port, 60000, but you can specify any port you want, or just remove that to start on a random port each time. Lastly, I've enabled URL rewriting, and pointing the URL rewrite configuration file to the urlrewrite.xml which is included in CFWheels 2.x. (If you've used the wheels new command to create your app, this will already be done for you).
You can also specify hosts other than localhost: there's a useful CommandBox module to do that () which will automatically create entries in your hosts file to allow for domains such as myapp.local running on port 80. You can install it via install commandbox-hostupdater when running the box shell with administrator privileges.
Obviously, anything you start, you might want to stop. Servers can be stopped either via right/ctrl clicking on the icon in the taskbar, or by the stop command. To stop a server running in the current directory issue the following:
server stop
You can also stop a server from anywhere by using its name:
server stop myApp
If you want to see what server configurations exist on your system and their current status, simply do server list
server list
To remove a server configuration from the list, you can use server forget myapp. Note the status of the servers on the list are somewhat unreliable, as it only remembers the last known state of the server: so if you start a server and then turn off your local machine, it may still remember it as running when you turn your local machine back on, which is why we recommend the use of force: true in the server.json file.
By default, CommandBox will run Lucee (version 5.x at time of writing). You may wish to specify an exact version of Lucee, or use Adobe ColdFusion. We can do this via either setting the appropriate cfengine setting in server.json, or at runtime with the cfengine= argument.
Start the default engine
CommandBox> start
__
Start the latest stable Lucee 5.x engine
CommandBox> start cfengine=lucee@5
__
Start a specific engine and version
CommandBox> start [email protected]
__
Start the most recent Adobe server that starts with version "11"
CommandBox> start cfengine=adobe@11
__
Start the most recent adobe engine that matches the range
CommandBox> start cfengine="adobe@>9.0 <=11"
Or via server.json
You can of course run multiple servers, so if you need to test your app on Lucee 4.x, Lucee 5.x and Adobe 2016, you can just start three servers with different cfengine= arguments!
By default the Lucee server that CommandBox starts has all the basic Lucee extensions that you are going to need installed, but if need to minimize the size of the Lucee instance you launch, then you can use Lucee-Light by specifying cfengine=lucee-light in your server.json file. CFWheels can run just fine on lucee-light (which is after all, Lucee, minus all the extensions) but at a minimum, requires the following extensions to be installed as dependencies in your box.json. Please note you may have to add any drivers you need for your database to this list as well.
Once added to your box.json file, while the server is running, just do box install, which will install the dependencies, and load them into the running server within 60 seconds.
Alternatively you can download the extensions and add them manually to your server root's deploy folder (i.e \WEB-INF\lucee-server\deploy)
{
"name":"myApp",
"force":true,
"web":{
"http":{
"port":60000
},
"rewrites":{
"enable":true,
"config":"urlrewrite.xml"
}
}
}myapp (stopped)
http://127.0.0.1:60000
Webroot: /Users/cfwheels/Documents/myapp
myAPI (stopped)
http://127.0.0.1:60010
Webroot: /Users/cfwheels/Documents/myAPI
megasite (stopped)
http://127.0.0.1:61280
CF Engine: lucee 4.5.4+017
Webroot: /Users/cfwheels/Documents/megasite
awesomesite (stopped)
http://127.0.0.1:60015
CF Engine: lucee 4.5.4+017
Webroot: /Users/cfwheels/Documents/awesomeo{
"name":"myApp",
"force":true,
"web":{
"http":{
"host":"localhost",
"port":60000
},
"rewrites":{
"enable":true,
"config":"urlrewrite.xml"
}
},
"app":{
"cfengine":"adobe@2016"
},
}"dependencies":{
"lucee-image":"lex:https://ext.lucee.org/lucee.image.extension-1.0.0.35.lex",
"lucee-zip": "lex:https://ext.lucee.org/compress-extension-1.0.0.2.lex",
"lucee-esapi": "lex:https://ext.lucee.org/esapi-extension-2.1.0.18.lex"
}In this tutorial, we'll be writing a simple application to make sure we have CFWheels installed properly and that everything is working as it should.
Let's make sure we're all on the same page. I'm going to assume that you've followed the guide and have CommandBox all setup. If you haven't done that, stop and read that guide get everything setup. It's okay, this web page will wait for you.
Okay, so you have CFWheels installed and can see the CFWheels "Congratulations!" page as shown below. That wasn't that hard now, was it?
Okay, let's get to some example code. We know that you've been dying to get your hands on some code!
To continue with Programming Tutorial Tradition, we'll create the ubiquitous Hello World! application. But to keep things interesting, let's add a little CFWheels magic along the way.
Let's create a controller from scratch to illustrate how easy it is to set up a controller and plug it into the CFWheels framework.
First, create a file called Say.cfc in the controllers directory and add the
code below to the file.
component extends="Controller"{
}Congratulations, you just created your first CFWheels controller! What does this
controller do, you might ask? Well, to be honest, not much. It has no methods
defined, so it doesn't add any new functionality to our application. But because
it extends the base Controller component, it inherits quite a bit of powerful
functionality and is now tied into our CFWheels application.
So what happens if we try to call our new controller right now? Lets take a
look. Open your browser and point your browser to the new controller. Because my
local server is installed on port 60000, my URL is http://127.0.0.1:60000/say.
You may need to enter a different URL, depending on how your web server is
configured. In my case, I'm using CommandBox.
The error says "Could not find the view page for the 'index' action in the 'say'
controller." Where did "index" come from? The URL we typed in only specified a
controller name but no action. When an action is not specified in the URL,
CFWheels assumes that we want the default action. Out of the box, the default
action in CFWheels is set to index. So in our example, CFWheels tried to find
the index action within the say controller, and it threw an error because it
couldn't find its view page.
But let's jump ahead. Now that we have the controller created, let's add an
action to it called hello. Change your say controller so it looks like the
code block below:
component extends="Controller" {
function hello() {
}
}As you can see, we created an empty method named hello.
Now let's call our new action in the browser and see what we get. To call the
hello action, we simply add /hello to the end of the previous URL that we
used to call our say controller:
http://127.0.0.1:60000/say/hello
Once again, we get a ColdFusion error. Although we have created the controller
and added the hello action to it, we haven't created the view.
By default, when an action is called, CFWheels will look for a view file with
the same name as the action. It then hands off the processing to the view to
display the user interface. In our case, CFWheels tried to find a view file for
our say/hello action and couldn't find one.
Let's remedy the situation and create a view file. View files are simple CFML pages that handle the output of our application. In most cases, views will return HTML code to the browser. By default, the view files will have the same name as our controller actions and will be grouped into a directory under the view directory. This new directory will have the same name as our controller.
Find the views directory in the root of your CFWheels installation. There will
be a few directories in there already. For now, we need to create a new
directory in the views directory called say. This is the same name as the
controller that we created above.
Now inside the say directory, create a file called hello.cfm. In the
hello.cfm file, add the following line of code:
<h1>Hello World!</h>Save your hello.cfm file, and let's call our say/hello action once again.
You have your first working CFWheels page if your browser looks like Figure 3
below.
You have just created your first functional CFWheels page, albeit it is a very simple one. Pat yourself on the back, go grab a snack, and when you're ready, let's go on and extend the functionality of our Hello World! application a little more.
We will add some simple dynamic content to our hello action and add a second
action to the application. We'll then use some CFWheels code to tie the 2
actions together. Let's get get to it!
The first thing we are going to do is to add some dynamic content to our
say/hello action. Modify your say controller so it looks like the code block
below:
component extends="Controller" {
function hello() {
time = Now();
}
}All we are doing here is creating a variable called time and setting its value
to the current server time using the basic ColdFusion Now() function. When we
do this, the variable becomes immediately available to our view code.
Why not just set up this value directly in the view? If you think about it, maybe the logic behind the value of time may eventually change. What if eventually we want to display its value based on the user's time zone? What if later we decide to pull it from a web service instead? Remember, the controller is supposed to coordinate all of the data and business logic, not the view.
Next, we will modify our say/hello.cfm view file so that it looks like the
code block bellow. When we do this, the value will be displayed in the browser.
<h1>Hello World!</h1>
<p>Current time: <cfoutput>#time#</cfoutput></p>call your say/hello action again in your browser. Your browser should look
like Figure 4 below.
This simple example showed that any dynamic content created in a controller
action is available to the corresponding view file. In our application, we
created a time variable in the say/hello controller action and display that
variable in our say/hello.cfm view file.
Now we will expand the functionality of our application once again by adding a
second action to our say controller. If you feel adventurous, go ahead and add
a goodbye action to the say controller on your own, then create a
goodbye.cfm view file that displays a "Goodbye" message to the user. If you're
not feeling that adventurous, we'll quickly go step by step.
First, modify the the say controller file so that it looks like the code block
below.
component extends="Controller" {
function hello() {
time = Now();
}
function goodbye() {
}
}Now go to the views/say directory and create a goodbye.cfm page.
Add the following code to the goodbye.cfm page and save it.
Goodbye World!If we did everything right, we should be able to call the new say/goodbye
action using the following URL:
http://127.0.0.1:60000/say/goodbye
Your browser should look like Figure 5 below:
Now let's link our two actions together. We will do this by adding a link to the bottom of each page so that it calls the other page.
Open the say/hello.cfm view file. We are going to add a line of code to the
end of this file so our say/hello.cfm view file looks like the code block
below:
<h1>Hello World!</h1>
<p>Current time: <cfoutput>#time#</cfoutput></p>
<p>Time to say <cfoutput>#linkTo(text="goodbye", action="goodbye")#?</cfoutput></p>The linkTo() function is a built-in CFWheels function. In this case, we are passing 2 named parameters to it. The first parameter, text, is the text
that will be displayed in the hyperlink. The second parameter, action, defines the action to point the link to. By using this built-in function, your application's main URL may change, and even controllers and actions may get shifted around, but you won't suffer from the dreaded dead link. CFWheels will
always create a valid link for you as long as you configure it correctly when you make infrastructure changes to your application.
Once you have added the additional line of code to the end of the
say/hello.cfm view file, save your file and call the say/hello action from
your browser. Your browser should look like Figure 6 below.
You can see that CFWheels created a link for us and added an appropriate URL for
the say/goodbye action to the link.
Let's complete our little app and add a corresponding link to the bottom of our
say/goodbye.cfm view page.
Open your say/goodbye.cfm view page and modify it so it looks like the code
block below.
CFML: views/say/goodbye.cfm
<h1>Goodbye World!</h1>
<p>Time to say <cfoutput>#linkTo(text="hello", action="hello")#?</cfoutput></p>If you now call the say/goodbye action in your browser, your browser should
look like Figure 7 below.
You now know enough to be dangerous with CFWheels. Look out! But there are many more powerful features to cover. You may have noticed that we haven't even talked about the M in MVC.
No worries. We will get there. And we think you will enjoy it.







A quick tutorial that demonstrates how quickly you can get database connectivity up and running with CFWheels.
CFWheels's built in model provides your application with some simple and powerful functionality for interacting with databases. To get started, you will make some simple configurations, call some functions within your controllers, and that's it. Best yet, you will rarely ever need to write SQL code to get those redundant CRUD tasks out of the way.
We'll learn by building part of a sample user management application. This tutorial will teach you the basics of setting up a resource that interacts with the CFWheels ORM.
By default, CFWheels will connect to a data source that has the same name as the folder containing your application. So if your application is in a folder called C:\websites\mysite\blog\, then it will connect to a data source named blog.
To change this default behavior, open the file at config/settings.cfm. In a fresh install of CFWheels, you'll see some commented-out lines of code that read as such:
<cfscript>
// Use this file to configure your application.
// You can also use the environment specific files (e.g. /config/production/settings.cfm) to override settings set here.
// Don't forget to issue a reload request (e.g. reload=true) after making changes.
// See http://docs.cfwheels.org/docs/configuration-and-defaults for more info.
// If you leave the settings below commented out, CFWheels will set the data source name to the same name as the folder the application resides in.
// set(dataSourceName="");
// set(dataSourceUserName="");
// set(dataSourcePassword="");
</cfscript>Uncomment the lines that tell CFWheels what it needs to know about the data source and provide the appropriate values. This may include values for dataSourceName, dataSourceUserName, and dataSourcePassword.
set(dataSourceName="back2thefuture");
// set(dataSourceUserName="marty");
// set(dataSourcePassword="mcfly");CFWheels supports MySQL, SQL Server, PostgreSQL, and H2. It doesn't matter which DBMS you use for this tutorial; we will all be writing the same CFML code to interact with the database. CFWheels does everything behind the scenes that needs to be done to work with each DBMS.
That said, here's a quick look at a table that you'll need in your database, named users:
id
int
auto increment
name
varchar(100)
varchar(255)
password
varchar(15)
Note a couple things about this users table:
The table name is plural.
The table has an auto-incrementing primary key named id.
These are database conventions used by CFWheels. This framework strongly encourages that everyone follow convention over configuration. That way everyone is doing things mostly the same way, leading to less maintenance and training headaches down the road.
Fortunately, there are ways of going outside of these conventions when you really need it. But let's learn the conventional way first. Sometimes you need to learn the rules before you can know how to break them.
Next, open the file at config/routes.cfm. You will see contents similar to this:
mapper()
.wildcard()
.root(to="wheels##wheels", method="get")
.end();We are going to create a section of our application for listing, creating, updating, and deleting user records. In CFWheels routing, this requires a plural resource, which we'll name users.
Because a users resource is more specific than the "generic" routes provided by CFWheels, we'll list it first in the chain of mapper method calls:
mapper()
.resources("users")
.wildcard()
.root(to="wheels##wheels", method="get")
.end();This will create URL endpoints for creating, reading, updating, and deleting user records:
users
GET
/users
Lists users
newUsers
GET
/users/new
Display a form for creating a user record
users
POST
/users
Form posts a new user record to be created
editUser
GET
/users/[id]/edit
Displays a form for editing a user record
user
PATCH
/users/[id]
Form posts an existing user record to be updated
user
DELETE
/users/[id]
Deletes a user record
Name is referenced in your code to tell CFWheels where to point forms and links.
Method is the HTTP verb that CFWheels listens for to match up the request.
URL Path is the URL that CFWheels listens for to match up the request.
First, let's create a simple form for adding a new user to the users table. To do this, we will use CFWheels's form helper functions. CFWheels includes a whole range of functions that simplifies all of the tasks that you need to display forms and communicate errors to the user.
Now create a new file in views/users called new.cfm. This will contain the view code for our simple form.
Next, add these lines of code to the new file:
<cfoutput>
<h1>New User</h1>
#startFormTag(route="users")#
<div>
#textField(objectName="user", property="name", label="Name")#
</div>
<div>
#textField(objectName="user", property="email", label="Email")#
</div>
<div>
#passwordField(
objectName="user",
property="password",
label="Password"
)#
</div>
<div>#submitTag()#</div>
#endFormTag()#
</cfoutput>What we've done here is use form helpers to generate all of the form fields necessary for creating a new user in our database. It may feel a little strange using functions to generate form elements, but it will soon become clear why we're doing this. Trust us on this one… you'll love it!
To generate the form tag's action attribute, the startFormTag() function takes parameters similar to the linkTo()function that we introduced in the Beginner Tutorial: Hello World tutorial. We can pass in controller, action, key, and other route- and parameter-defined URLs just like we do with linkTo().
To end the form, we use the endFormTag() function. Easy enough.
The textField() and passwordField() helpers are similar. As you probably guessed, they create <input> elements with type="text" and type="password", respectively. And the submitTag() function creates an <input type="submit" /> element.
One thing you'll notice is the textField() and passwordField() functions accept arguments called objectName and property. As it turns out, this particular view code will throw an error because these functions are expecting an object named user. Let's fix that.
All of the form helper calls in our view specify an objectName argument with a reference to a variable named user. That means that we need to supply our view code with an object called user. Because the controller is responsible for providing the views with data, we'll set it there.
Create a new ColdFusion component at controllers/Users.cfc.
As it turns out, our controller needs to provide the view with a blank user object (whose instance variable will also be called user in this case). In our new action, we will use the model() function to generate a new instance of the user model.
To get a blank set of properties in the model, we'll also call the generated model's new() method.
component extends="Controller" {
function config(){}
function new() {
user = model("user").new();
}
}CFWheels will automatically know that we're talking about the users database table when we instantiate a usermodel. The convention: database tables are plural and their corresponding CFWheels models are singular.
Why is our model name singular instead of plural? When we're talking about a single record in the users database, we represent that with an individual model object. So the users table contains many user objects. It just works better in conversation.
Now when we run the URL at http://localhost/users/new, we'll see the form with the fields that we defined.
The HTML generated by your application will look something like this:
<h1>New User</h1>
<form action="/users" method="post">
<div>
<label for="user-name">
Name
<input id="user-name" type="text" value="" name="user[name]">
</label>
</div>
<div>
<label for="user-email">
Email
<input id="user-email" type="text" value="" name="user[email]">
</label>
</div>
<div>
<label for="user-password">
Password
<input id="user-password" type="password" value="" name="user[password]">
</label>
</div>
<div><input value="Save changes" type="submit"></div>
</form>So far we have a fairly well-formed, accessible form, without writing a bunch of repetitive markup.
Next, we'll code the create action in the controller to handle the form submission and save the new user to the database.
A basic way of doing this is using the model object's create() method:
function create() {
user = model("user").create(params.user);
redirectTo(
route="users",
success="User created successfully."
);
}Because we used the objectName argument in the fields of our form, we can access the user data as a struct in the params struct.
There are more things that we can do in the create action to handle validation, but let's keep it simple in this tutorial.
Notice that our create action above redirects the user to the users index route using the redirectTo() function. We'll use this action to list all users in the system with "Edit" links. We'll also provide a link to the "New User" form that we just coded.
First, let's get the data that the listing needs. Create an action named index in the users controller like so:
function index() {
users = model("user").findAll(order="name");
}This call to the model's findAll() method will return a query object of all users in the system. By using the method's order argument, we're also telling the database to order the records by name.
In the view at views/users/index.cfm, it's as simple as looping through the query and outputting the data
<cfoutput>
<h1>Users</h1>
<p>#linkTo(text="New User", route="newUser")#</p>
<table>
<thead>
<tr>
<th>Name</th>
<th>Email</th>
<th colspan="2"></th>
</tr>
</thead>
<tbody>
<cfloop query="users">
<tr>
<td>
#EncodeForHtml(users.name)#
</td>
<td>
#EncodeForHtml(users.email)#
</td>
<td>
#linkTo(
text="Edit",
route="editUser",
key=users.id,
title="Edit #users.name#"
)#
</td>
<td>
#buttonTo(
text="Delete",
route="user",
key=users.id,
method="delete",
title="Delete #users.name#"
)#
</td>
</tr>
</cfloop>
</tbody>
</table>
</cfoutput>We'll now show another cool aspect of form helpers by creating a screen for editing users.
You probably noticed in the code listed above that we'll have an action for editing a single users record. We used the linkTo() form helper function to add an "Edit" button to the form. This action expects a key as well.
Because in the linkTo() form helper function we specified the parameter key, Wheels adds this parameter into the URL when generating the route.
Wheels will automatically add the provided 'key' from the URL to the params struct in the controllers edit() function.
Given the provided key, we'll have the action load the appropriate user object to pass on to the view:
function edit() {
user = model("user").findByKey(params.key);
}The view at views/user/edit.cfm looks almost exactly the same as the view for creating a user:
<cfoutput>
<h1>Edit User #EncodeForHtml(user.name)#</h1>
#startFormTag(route="user", key=user.key(), method="patch")#
<div>
#textField(objectName="user", property="name", label="Name")#
</div>
<div>
#textField(objectName="user", property="email", label="Email")#
</div>
<div>
#passwordField(
objectName="user",
property="password",
label="Password"
)#
</div>
<div>#submitTag()#</div>
#endFormTag()#
</cfoutput>But an interesting thing happens. Because the form fields are bound to the user object via the form helpers' objectName arguments, the form will automatically provide default values based on the object's properties.
With the user model populated, we'll end up seeing code similar to this:
<h1>Edit User Homer Simpson</h1>
<form action="/users/1" method="post">
<input type="hidden" name="_method" value="patch">
<div>
<input type="hidden" name="user[id]" value="15">
</div>
<div>
<label for="user-name">
Name
<input
id="user-name"
type="text"
value="Homer Simpson"
name="user[name]">
</label>
</div>
<div>
<label for="user-email">
Email
<input
id="user-email"
type="text"
value="[email protected]"
name="user[email]">
</label>
</div>
<div>
<label for="user-password">
Password
<input
id="user-password"
type="password"
value="donuts.mmm"
name="user[password]">
</label>
</div>
<div><input value="Save changes" type="submit"></div>
</form>Pretty cool, huh?
There's a lot of repetition in the new and edit forms. You'd imagine that we could factor out most of this code into a single view file. To keep this tutorial from becoming a book, we'll just continue on knowing that this could be better.
Now we'll create the update action. This will be similar to the create action, except it will be updating the user object:
function update() {
user = model("user").findByKey(params.key);
user.update(params.user);
redirectTo(
route="editUser",
key=user.id,
success="User updated successfully."
);
}To update the user, simply call its update() method with the user struct passed from the form via params. It's that simple.
After the update, we'll add a success message using the Flash and send the end user back to the edit form in case they want to make more changes.
Notice in our listing above that we have a delete action. Here's what it would look like:
function delete() {
user = model("user").findByKey(params.key);
user.delete();
redirectTo(
route="users",
success="User deleted successfully."
);
}We simply load the user using the model's findByKey() method and then call the object's delete() method. That's all there is to it.
We've shown you quite a few of the basics in getting a simple user database up and running. We hope that this has whet your appetite to see some of the power packed into the CFWheels framework. There's plenty more.
Be sure to read on to some of the related chapters listed below to learn more about working with CFWheels's ORM.