All pages
Powered by GitBook
1 of 5

Loading...

Loading...

Loading...

Loading...

Loading...

Getting Started

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.

Install CommandBox

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.

Install the cfwheels-cli CommandBox Module

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.

Start a new Application

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.

A Word About Command Aliases

CommandBox commands have the capability to be called by multiple names or aliases. The command above wheels generate app can also be initiated by typing wheels g app. In fact g is an alias for generate so wherever you see a command in the CLI documentation that has generate in it you can substitute g instead.

In addition to shortening generate to g, aliases can completely change the name space as well. A command that you haven't seen yet is the wheels generate app-wizard command. This command guides the user through a series of menu options, building up all the parameters needed to customize the start of a new CFWheels project. You're likely to use the wizard when starting a new CFWheels application so it's good to become familiar with it.

This command has the normal alias referenced above at wheels g app-wizard but it also has an additional alias at wheels new which is the command more prevalent in the Rails community. So the three commands wheels generate app-wizard, wheels g app-wizard, and wheels new all call the same functionality which guides the user though a set of menus, collecting details on how to configure the desired app. Once all the parameters have been gathered, this command actually calls the wheels generate app command to create the actual CFWheels application.

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.

Tutorial: CFWheels, AJAX, and You

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.

A Simple Example

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:

config/routes.cfm
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:

views/say/hello.cfm
<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() ):

JavaScript
(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:

controllers/Say.cfc
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.

AJAX in CFWheels Explained

That is it! Hopefully now you have a clearer picture on how to create AJAX-based features for your web applications.

Running Local Development servers

Starting a local development server

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).

Using custom host names

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.

Controlling local servers

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.

Specifying different CF engines

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

CFIDE / Lucee administrators

The default username and password for all administrators is admin & commandbox

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!

Watch out

CommandBox 5.1 required to install dependencies easily

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"
}
Host updater

Beginner Tutorial: Hello World

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.

Testing Your Install

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?

Figure 1: Wheels congratulations screen

Hello World: Your First CFWheels App

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.

Setting up the Controller

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.

controllers/Say.cfc
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.

Figure 2: Wheels error after setting up your blank say controller

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.

Setting up an Action

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:

controllers/Say.cfc
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.

Setting up 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:

views/say/hello.cfm
<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.

Figure 3: Your first working CFWheels action.

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.

Adding Dynamic Content to Your View

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 Dynamic Content

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:

controllers/Say.cfc
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.

Displaying the Dynamic Content

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.

views/say/hello.cfm
<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.

Figure 4: Hello World with the current date and time

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.

Adding a Second Action: Goodbye

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.

controllers/Say.cfc
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.

views/say/goodbye.cfm
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:

Figure 5: Your new goodbye action

Linking to Other Actions

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.

Linking Hello to Goodbye

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:

views/say/hello.cfm
<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.

Figure 6: Your say/hello action with a link to the goodbye action

You can see that CFWheels created a link for us and added an appropriate URL for the say/goodbye action to the link.

Linking Goodbye to Hello

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

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.

Figure 7: Your say/goodbye action with a link to the hello action

Much More to Learn

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.

Beginner Tutorial: Hello Database

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.

Our Sample Application: User Management

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.

Download source code

You can download all the source code for this sample application from https://github.com/dhgassoc/Cfwheels-Beginner-Tutorial-Hello-Database

Setting up the Data Source

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:

config/settings.cfm
<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.

config/settings.cfm
set(dataSourceName="back2thefuture");
// set(dataSourceUserName="marty");
// set(dataSourcePassword="mcfly");

Our Sample Data Structure

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:

Column Name
Data Type
Extra

id

int

auto increment

name

varchar(100)

email

varchar(255)

password

varchar(15)

Note a couple things about this users table:

  1. The table name is plural.

  2. 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.

Creating Routes for the users Resource

Next, open the file at config/routes.cfm. You will see contents similar to this:

config/routes.cfm
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:

config/routes.cfm
mapper()
    .resources("users")
    .wildcard()
    .root(to="wheels##wheels", method="get")
.end();

This will create URL endpoints for creating, reading, updating, and deleting user records:

Name
Method
URL Path
Description

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.

Don't forget to reload

You will need to reload your application after adding new routes!

Creating Users

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.

Creating the Form

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:

views/users/new.cfm
<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>

Form Helpers

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.

Supplying the Form with Data

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.

controllers/Users.cfc
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.

The Generated Form

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:

/users/new
<h1>New User</h1>

<form action="/users" method="post">
    <div>
        <label for="user-name">
            Name
            <input id="user-name" type="text" value="" name="user&#x5b;name&#x5d;">
        </label>
    </div>

    <div>
        <label for="user-email">
            Email
            <input id="user-email" type="text" value="" name="user&#x5b;email&#x5d;">
        </label>
    </div>

    <div>
        <label for="user-password">
            Password
            <input id="user-password" type="password" value="" name="user&#x5b;password&#x5d;">
        </label>
    </div>

    <div><input value="Save&#x20;changes" type="submit"></div>
</form>

So far we have a fairly well-formed, accessible form, without writing a bunch of repetitive markup.

Handling the Form Submission

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:

controllers/Users.cfc
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.

Listing Users

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:

controllers/Users.cfc
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

views/users/index.cfm
<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>

When to use EncodeForHtml

You'll see references to EncodeForHtml in some of our examples that output data. This helps escape HTML code in data that attackers could use to embed inject harmful JavaScript. (This is commonly referred to as an "XSS attack," short for "Cross-site Scripting attack.")

A rule of thumb: you do not need to use EncodeForHtml when passing values into CFWheels helpers like linkTo, buttonTo, startFormTag, textField, etc. However, you need to escape data that is displayed directly onto the page without a CFWheels helper.

Editing Users

We'll now show another cool aspect of form helpers by creating a screen for editing users.

Coding the Edit Form

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:

controllers/Users.cfc
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:

views/users/edit.cfm
<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:

views/users/edit.cfm
<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&#x5b;id&#x5d;" value="15">
    </div>

    <div>
        <label for="user-name">
            Name
            <input
                id="user-name"
                type="text"
                value="Homer Simpson"
                name="user&#x5b;name&#x5d;">
        </label>
    </div>

    <div>
        <label for="user-email">
            Email
            <input
                id="user-email"
                type="text"
                value="[email protected]"
                name="user&#x5b;email&#x5d;">
        </label>
    </div>

    <div>
        <label for="user-password">
            Password
            <input
                id="user-password"
                type="password"
                value="donuts.mmm"
                name="user&#x5b;password&#x5d;">
        </label>
    </div>

    <div><input value="Save&#x20;changes" type="submit"></div>
</form>

Pretty cool, huh?

Opportunities for Refactoring

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.

Handing the Edit Form Submission

Now we'll create the update action. This will be similar to the create action, except it will be updating the user object:

controllers/Users.cfc
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.

Deleting Users

Notice in our listing above that we have a delete action. Here's what it would look like:

controllers/Users.cfc
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.

Database Says Hello

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.

Getting Started