wheels generate controller
Generate a controller with actions and optional views.
Synopsis
wheels generate controller name=<controllerName> [options]
#Can also be used as:
wheels g controller name=<controllerName> [options]CommandBox Parameter Syntax
This command supports multiple parameter formats:
Positional parameters:
wheels generate controller Products(controller name)Named parameters:
name=value(e.g.,name=Products,actions=index,show)Flag parameters:
--flagequalsflag=true(e.g.,--crudequalscrud=true)Flag with value:
--flag=valueequalsflag=value(e.g.,--actions=index,show)
Parameter Mixing Rules:
ALLOWED:
Positional:
wheels generate controller ProductsPositional + flags:
wheels generate controller Products --crud --apiAll named:
name=Products actions=index,show
NOT ALLOWED:
Positional + named:
wheels generate controller Products actions=index(causes error)
Recommendation: Use positional for controller name, flags for options: wheels generate controller Products --crud
Note: Flag syntax (--flag) avoids positional/named parameter conflicts and is recommended for boolean options.
Important: Use --actions (plural) not --action (singular) for custom action lists.
Description
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.
Arguments
name
Name of the controller to create (usually plural)
Required
Options
actions
Actions to generate (comma-delimited) - HIGHEST PRIORITY, overrides --crud
index
crud
Generate CRUD controller with actions (index, show, new, create, edit, update, delete) and scaffold-style views (index, show, new, edit, _form)
false
api
Generate API controller (no views generated, only JSON/XML endpoints)
false
noViews
Skip view generation (only generate controller)
false
description
Controller description comment
""
force
Overwrite existing files
false
Parameter Priority and Behavior
Understanding how parameters interact is important for getting the expected results.
Default Behavior
The controller generator creates views for ALL actions by default (unless you use --api or --noViews).
Parameter Priority
The command evaluates parameters in the following order:
PRIORITY 1 (HIGHEST): --actions
└─> If specified, generates ONLY the specified actions
└─> Overrides --crud and --api
└─> Example: --actions=dashboard,reports
PRIORITY 2: --crud
└─> If specified (and --actions not used), generates 7 CRUD actions:
- index, show, new, create, edit, update, delete
└─> Generates 5 scaffold-style views:
- index.cfm, show.cfm, new.cfm, edit.cfm, _form.cfm
PRIORITY 3: --api
└─> If specified (and --actions not used), generates 5 API actions:
- index, show, create, update, delete
└─> Implies --crud but excludes form actions (new, edit)
└─> Implies --noViews (no views generated)
PRIORITY 4 (DEFAULT): No flags
└─> Generates only 1 action: index
└─> Generates 1 view: index.cfmView Generation Rules
Views are generated for actions EXCEPT when:
--apiflag is used (API controllers return JSON/XML, not HTML)--noViewsflag is used (explicitly skip view generation)
Views ARE generated by default for:
Controllers with
--crudflag (5 scaffold-style views)Controllers with custom
--actions(1 view per action)Controllers with no flags (1 view for index action)
Parameter Combinations
Basic controller (no flags)
wheels generate controller Products
# Result:
# Actions: index
# Views: index.cfmCustom actions
wheels generate controller Products --actions=dashboard,reports,export
# Result:
# Actions: dashboard, reports, export
# Views: dashboard.cfm, reports.cfm, export.cfmCRUD controller with scaffold-style views
wheels generate controller Products --crud
# Result:
# Actions: index, show, new, create, edit, update, delete (7 actions)
# Views: index.cfm, show.cfm, new.cfm, edit.cfm, _form.cfm (5 views)
# Note: Same views as "wheels generate scaffold"Custom actions without views
wheels generate controller Products --actions=dashboard,export --noViews
# Result:
# Actions: dashboard, export
# Views: None (explicitly skipped)API controller (no views)
wheels generate controller Orders --api
# Result:
# Actions: index, show, create, update, delete (5 actions)
# Views: None (API returns JSON/XML)CRUD controller without views
wheels generate controller Products --crud --noViews
# Result:
# Actions: index, show, new, create, edit, update, delete (7 actions)
# Views: None (explicitly skipped)Custom actions override CRUD
wheels generate controller Products --actions=dashboard,reports --crud
# Result:
# Actions: dashboard, reports (--actions has priority)
# Views: dashboard.cfm, reports.cfm (views still created)
# Note: --crud is ignored when --actions is specifiedImportant Rules
Views generated by default - Unless you use
--apior--noViews, views are created for ALL actions--actionsoverrides--crud- Custom actions take priority, but views are STILL generated--apiimplies--crudand--noViews- API controllers get 5 actions, no views--noViewsskips all views - Use when you only need the controller file--crudgenerates scaffold-style views - Same 5 views aswheels generate scaffoldcommand
Decision Tree
ACTION GENERATION:
├─ Has --actions? → Use those actions ONLY (highest priority)
├─ 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 ACTIONCommon Use Cases
Traditional web app (scaffold-style)
--crud
7
5 (index, show, new, edit, _form)
REST API (JSON/XML)
--api
5
None
Single page controller
(no flags)
1 (index)
1 (index)
Custom actions with views
--actions=dashboard,export
2
2 (dashboard, export)
Controller only (no views)
--crud --noViews
7
None
Custom without views
--actions=api,process --noViews
2
None
When to Use What
Use
--crud- Traditional web app with CRUD operations, forms, and.resources()routing (same as scaffold)Use
--api- REST API that returns JSON/XML (no views)Use
--actions- Custom actions with automatic view generation (highest priority)Use
--noViews- When you only need controller file (manual view management)Use no flags - Simple controller with index action and view
Key Difference: --crud vs --api
Both generate RESTful controllers with different purposes:
Purpose
Traditional web application
API endpoints
Actions
7 (includes new, edit forms)
5 (no form actions)
Views
5 scaffold-style views
None
View Files
index, show, new, edit, _form
N/A
Routing
Use .resources() in routes.cfm
Use .resources() in routes.cfm
Response
HTML pages with forms
JSON/XML data
Use Case
User-facing web apps
Mobile apps, SPAs, integrations
Important: --crud generates the same views as wheels generate scaffold command, including the _form.cfm partial for form code reuse.
Examples:
# CRUD web app with scaffold-style views
wheels generate controller Products --crud
# Result: 7 actions + 5 views
# Use for: Traditional web applications with HTML forms
# REST API without views
wheels generate controller Products --api
# Result: 5 actions + 0 views
# Use for: Mobile apps, single-page apps, third-party integrationsExamples
Parameter Priority Examples
Understanding how --actions overrides --crud:
# Example 1: CRUD flag generates 7 actions and 5 views
wheels generate controller Products --crud
# Result:
# Actions: index, show, new, create, edit, update, delete (7 actions)
# Views: index.cfm, show.cfm, new.cfm, edit.cfm, _form.cfm (5 views)
# Example 2: Actions flag OVERRIDES crud (HIGHEST PRIORITY)
wheels generate controller Products --crud --actions=index
# Result:
# Actions: index (only this action)
# Views: index.cfm (only this view)
# Note: --actions has HIGHEST PRIORITY, --crud is ignored
# Example 3: Multiple custom actions override crud
wheels generate controller Products --crud --actions=dashboard,reports
# Result:
# Actions: dashboard, reports (only these actions)
# Views: dashboard.cfm, reports.cfm (only these views)
# Note: --actions overrides --crud completelyCommon Mistake: Using --action (singular) instead of --actions (plural)
# WRONG - will not work
wheels generate controller Products --action=index
# CORRECT - use plural --actions
wheels generate controller Products --actions=indexBasic controller
# Positional (recommended)
wheels generate controller Products
# OR named
wheels g controller name=ProductsCreates:
app/controllers/Products.cfcwithindexactionapp/views/products/index.cfmview
Controller with custom actions
# Positional + flag (recommended)
wheels generate controller Products --actions=dashboard,reports,export
# OR all named
wheels g controller name=Products actions=dashboard,reports,exportCreates:
app/controllers/Products.cfcwith 3 custom actionsapp/views/products/dashboard.cfmapp/views/products/reports.cfmapp/views/products/export.cfm
CRUD controller (scaffold-style views)
# Positional + flag (recommended)
wheels generate controller Products --crud
# OR all named
wheels g controller name=Products crud=trueCreates:
app/controllers/Products.cfcwith 7 CRUD actions5 scaffold-style views:
index.cfm,show.cfm,new.cfm,edit.cfm,_form.cfm
Note: The _form.cfm partial is shared between new and edit views, just like in wheels generate scaffold.
API controller (no views)
# Positional + flag (recommended)
wheels generate controller Orders --api
# OR all named
wheels g controller name=Orders api=trueCreates:
app/controllers/Orders.cfcwith 5 API actionsNo views (API controllers don't need views)
Controller without views (noViews flag)
# Positional + flag (recommended)
wheels generate controller Products --crud --noViews
# OR all named
wheels g controller name=Products rest=true noViews=trueCreates:
app/controllers/Products.cfcwith 7 RESTful actionsNo views (explicitly skipped with --noViews)
Custom actions without views
# Positional + flags (recommended)
wheels generate controller Reports --actions=dashboard,export --noViews
# OR all named
wheels g controller name=Reports actions=dashboard,export noViews=trueCreates:
app/controllers/Reports.cfcwith 2 custom actionsNo views (explicitly skipped)
Generated Code
Basic Controller
component extends="Controller" {
/**
* Controller config settings
**/
function config() {
}
/**
* index action
*/
function index() {
// TODO: Implement index action
}
}Controller with Description
/**
* Handles user management operations
*/
component extends="Controller" {
/**
* Controller config settings
**/
function config() {
}
/**
* index action
*/
function index() {
// TODO: Implement index action
}
}RESTful Controller
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 Controller
/**
* 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";
}
}View Generation
Views are automatically generated for non-API controllers:
index.cfm
<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>Naming Conventions
Controller names: PascalCase, typically plural (Products, Users)
Action names: camelCase (index, show, createProduct)
File locations:
Controllers:
/controllers/Nested:
/controllers/admin/Products.cfcViews:
/views/{controller}/
Routes Configuration
Add routes in /config/routes.cfm:
Traditional Routes
<cfscript>
mapper()
.get(name="products", to="products##index")
.get(name="product", to="products##show")
.post(name="products", to="products##create")
.wildcard()
.end();
</cfscript>RESTful Resources
<cfscript>
mapper()
.resources("products")
.wildcard()
.end();
</cfscript>Testing
Generate tests alongside controllers:
wheels generate controller name=products --crud
wheels generate test controller name=productsBest Practices
Use plural names for resource controllers
Keep controllers focused on single resources
Use
--crudfor standard web app CRUD operations (with views and forms)Use
--apifor API endpoints (JSON/XML, no views)Use
--actionswhen you need custom actions (HIGHEST PRIORITY - overrides--crud)Implement proper error handling
Add authentication in
config()methodUse filters for common functionality
Common Patterns
Authentication Filter
function config() {
filters(through="authenticate", except="index,show");
}
private function authenticate() {
if (!session.isLoggedIn) {
redirectTo(controller="sessions", action="new");
}
}Pagination
function index() {
products = model("Product").findAll(
page=params.page ?: 1,
perPage=25,
order="createdAt DESC"
);
}Search
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();
}
}See Also
wheels generate model - Generate models
wheels generate view - Generate views
wheels scaffold - Generate complete CRUD
wheels generate test - Generate controller tests
Last updated
Was this helpful?

