Skip to content

Command Line Tools

wheels generate controller

Generate a controller with actions and optional views.

Terminal window
wheels generate controller name=<controllerName> [options]
#Can also be used as:
wheels g controller name=<controllerName> [options]
  • Positional parameters: wheels generate controller Products (controller name)
  • Named parameters: param=value (e.g., name=Products, actions=index,show)
  • Flag parameters: --flag equals flag=true (e.g., --crud equals crud=true)
  • Params with value: --param=value equals param=value (e.g., --actions=index,show)

Recommended Format:

  • Positional for name: wheels generate controller Products
  • Flags for options: wheels generate controller Products --crud --force

Not Allowed:

  • Use --actions (plural) not --action (singular)
  • Don’t mix positional and named parameters (causes errors)

The wheels generate controller command creates a new controller CFC file with specified actions and optionally generates corresponding view files. It supports both traditional and RESTful controller patterns.

ArgumentDescriptionDefault
nameName of the controller to create (usually plural)Required
OptionDescriptionDefault
actionsActions to generate (comma-delimited) - HIGHEST PRIORITY, overrides --crudindex
crudGenerate CRUD controller with actions (index, show, new, create, edit, update, delete) and scaffold-style views (index, show, new, edit, _form)false
apiGenerate API controller (no views generated, only JSON/XML endpoints)false
noViewsSkip view generation (only generate controller)false
descriptionController description comment""
forceOverwrite existing filesfalse
ACTION GENERATION:
├─ Has --actions? → Use those actions ONLY (highest priority, overrides --crud)
├─ Has --api? → Generate 5 actions (index, show, create, update, delete)
├─ Has --crud? → Generate 7 actions (index, show, new, create, edit, update, delete)
└─ Default → Generate 1 action (index)
VIEW GENERATION:
├─ Has --api? → NO VIEWS (JSON/XML responses)
├─ Has --noViews? → NO VIEWS (explicitly skipped)
├─ Has --crud? → 5 VIEWS (index, show, new, edit, _form)
└─ Default → CREATE 1 VIEW PER ACTION
What You WantCommandActionsViews
Traditional web app (scaffold-style)--crud75 (index, show, new, edit, _form)
REST API (JSON/XML)--api5None
Single page controller(no flags)1 (index)1 (index)
Custom actions with views--actions=dashboard,export22 (dashboard, export)
Controller only (no views)--crud --noViews7None
Aspect—crud—api
PurposeTraditional web applicationAPI endpoints
Actions7 (includes new, edit forms)5 (no form actions)
Views5 scaffold-style viewsNone
ResponseHTML pages with formsJSON/XML data
Use CaseUser-facing web appsMobile apps, SPAs, integrations
Terminal window
wheels generate controller Products

Creates: Products.cfc with index action and index.cfm view

Terminal window
wheels generate controller Products --actions=dashboard,reports,export

Creates: Products.cfc with 3 custom actions and 3 views

Terminal window
wheels generate controller Products --crud

Creates: Products.cfc with 7 CRUD actions + 5 views (index, show, new, edit, _form)

Terminal window
wheels generate controller Orders --api

Creates: Orders.cfc with 5 API actions, no views

Terminal window
wheels generate controller Products --crud --noViews

Creates: Products.cfc with 7 actions, no views

Terminal window
wheels generate controller Products --crud --actions=dashboard

Creates: Products.cfc with only dashboard action (—actions overrides —crud)

component extends="Controller" {
/**
* Controller config settings
**/
function config() {
}
/**
* index action
*/
function index() {
// TODO: Implement index action
}
}
/**
* Handles user management operations
*/
component extends="Controller" {
/**
* Controller config settings
**/
function config() {
}
/**
* index action
*/
function index() {
// TODO: Implement index action
}
}
component extends="Controller" {
function config() {
verifies(except="index,new,create", params="key", paramsTypes="integer", handler="objectNotFound");
}
/**
* View all Products
**/
function index() {
products=model("product").findAll();
}
/**
* View Product
**/
function show() {
product=model("product").findByKey(params.key);
}
/**
* Add New Product
**/
function new() {
product=model("product").new();
}
/**
* Create Product
**/
function create() {
product=model("product").create(params.product);
if(product.hasErrors()){
renderView(action="new");
} else {
redirectTo(action="index", success="Product successfully created");
}
}
/**
* Edit Product
**/
function edit() {
product=model("product").findByKey(params.key);
}
/**
* Update Product
**/
function update() {
product=model("product").findByKey(params.key);
if(product.update(params.product)){
redirectTo(action="index", success="Product successfully updated");
} else {
renderView(action="edit");
}
}
/**
* Delete Product
**/
function delete() {
product=model("product").deleteByKey(params.key);
redirectTo(action="index", success="Product successfully deleted");
}
/**
* Redirect away if verifies fails, or if an object can't be found
**/
function objectNotFound() {
redirectTo(action="index", error="That Product wasn't found");
}
}
/**
* API endpoint for order processing
*/
component extends="wheels.Controller" {
function init() {
provides("json");
filters(through="setJsonResponse");
}
/**
* GET /orders
* Returns a list of all orders
*/
function index() {
local.orders = model("order").findAll();
renderWith(data={ orders=local.orders });
}
/**
* GET /orders/:key
* Returns a specific order by ID
*/
function show() {
local.order = model("order").findByKey(params.key);
if (IsObject(local.order)) {
renderWith(data={ order=local.order });
} else {
renderWith(data={ error="Record not found" }, status=404);
}
}
/**
* POST /orders
* Creates a new order
*/
function create() {
local.order = model("order").new(params.order);
if (local.order.save()) {
renderWith(data={ order=local.order }, status=201);
} else {
renderWith(data={ error="Validation failed", errors=local.order.allErrors() }, status=422);
}
}
/**
* PUT /orders/:key
* Updates an existing order
*/
function update() {
local.order = model("order").findByKey(params.key);
if (IsObject(local.order)) {
local.order.update(params.order);
if (local.order.hasErrors()) {
renderWith(data={ error="Validation failed", errors=local.order.allErrors() }, status=422);
} else {
renderWith(data={ order=local.order });
}
} else {
renderWith(data={ error="Record not found" }, status=404);
}
}
/**
* DELETE /orders/:key
* Deletes a order
*/
function delete() {
local.order = model("order").findByKey(params.key);
if (IsObject(local.order)) {
local.order.delete();
renderWith(data={}, status=204);
} else {
renderWith(data={ error="Record not found" }, status=404);
}
}
/**
* Set Response to JSON
*/
private function setJsonResponse() {
params.format = "json";
}
}

Views are automatically generated for non-API controllers:

<h1>Products</h1>
<p>#linkTo(text="New Product", action="new")#</p>
<table>
<thead>
<tr>
<th>Name</th>
<th>Actions</th>
</tr>
</thead>
<tbody>
<cfloop query="products">
<tr>
<td>#products.name#</td>
<td>
#linkTo(text="Show", action="show", key=products.id)#
#linkTo(text="Edit", action="edit", key=products.id)#
#linkTo(text="Delete", action="delete", key=products.id, method="delete", confirm="Are you sure?")#
</td>
</tr>
</cfloop>
</tbody>
</table>
  • Controller names: PascalCase, typically plural (Products, Users)
  • Action names: camelCase (index, show, createProduct)
  • File locations:
    • Controllers: /controllers/
    • Nested: /controllers/admin/Products.cfc
    • Views: /views/{controller}/

Add routes in /config/routes.cfm:

<cfscript>
mapper()
.get(name="products", to="products##index")
.get(name="product", to="products##show")
.post(name="products", to="products##create")
.wildcard()
.end();
</cfscript>
<cfscript>
mapper()
.resources("products")
.wildcard()
.end();
</cfscript>

Generate tests alongside controllers:

Terminal window
wheels generate controller name=products --crud
wheels generate test controller name=products
  1. Use plural names for resource controllers
  2. Keep controllers focused on single resources
  3. Use --crud for standard web app CRUD operations (with views and forms)
  4. Use --api for API endpoints (JSON/XML, no views)
  5. Use --actions when you need custom actions (HIGHEST PRIORITY - overrides --crud)
  6. Implement proper error handling
  7. Add authentication in config() method
  8. Use filters for common functionality
function config() {
filters(through="authenticate", except="index,show");
}
private function authenticate() {
if (!session.isLoggedIn) {
redirectTo(controller="sessions", action="new");
}
}
function index() {
products = model("Product").findAll(
page=params.page ?: 1,
perPage=25,
order="createdAt DESC"
);
}
function index() {
if (StructKeyExists(params, "q")) {
products = model("Product").findAll(
where="name LIKE :search OR description LIKE :search",
params={search: "%#params.q#%"}
);
} else {
products = model("Product").findAll();
}
}