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: --flag equals flag=true (e.g., --crud equals crud=true)

  • Flag with value: --flag=value equals flag=value (e.g., --actions=index,show)

Parameter Mixing Rules:

ALLOWED:

  • Positional: wheels generate controller Products

  • Positional + flags: wheels generate controller Products --crud --api

  • All 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

Argument
Description
Default

name

Name of the controller to create (usually plural)

Required

Options

Option
Description
Default

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

View Generation Rules

Views are generated for actions EXCEPT when:

  • --api flag is used (API controllers return JSON/XML, not HTML)

  • --noViews flag is used (explicitly skip view generation)

Views ARE generated by default for:

  • Controllers with --crud flag (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.cfm

Custom actions

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

# Result:
# Actions: dashboard, reports, export
# Views: dashboard.cfm, reports.cfm, export.cfm

CRUD 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 specified

Important Rules

  1. Views generated by default - Unless you use --api or --noViews, views are created for ALL actions

  2. --actions overrides --crud - Custom actions take priority, but views are STILL generated

  3. --api implies --crud and --noViews - API controllers get 5 actions, no views

  4. --noViews skips all views - Use when you only need the controller file

  5. --crud generates scaffold-style views - Same 5 views as wheels generate scaffold command

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 ACTION

Common Use Cases

What You Want
Command
Actions
Views

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:

Aspect
--crud
--api

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 integrations

Examples

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 completely

Common 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=index

Basic controller

# Positional (recommended)
wheels generate controller Products

# OR named
wheels g controller name=Products

Creates:

  • app/controllers/Products.cfc with index action

  • app/views/products/index.cfm view

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,export

Creates:

  • app/controllers/Products.cfc with 3 custom actions

  • app/views/products/dashboard.cfm

  • app/views/products/reports.cfm

  • app/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=true

Creates:

  • app/controllers/Products.cfc with 7 CRUD actions

  • 5 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=true

Creates:

  • app/controllers/Orders.cfc with 5 API actions

  • No 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=true

Creates:

  • app/controllers/Products.cfc with 7 RESTful actions

  • No 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=true

Creates:

  • app/controllers/Reports.cfc with 2 custom actions

  • No 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.cfc

    • Views: /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=products

Best Practices

  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

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"
    );
}
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

Last updated

Was this helpful?