Use this method to specify which properties can be set through mass assignment.
Name
Type
Required
Default
Description
properties
string
No
Property name (or list of property names) that are allowed to be altered through mass assignment.
// 1. Allow only `isActive` to be set through mass assignment (e.g. `updateAll()`, `new()`, `update()`).
config() {
accessibleProperties("isActive");
}
// 2. Allow a comma-delimited list of properties to be set through mass assignment.
// Any property not in this list is silently ignored when set via mass assignment.
config() {
accessibleProperties("firstName,lastName,email");
}
adds a column to existing table
Only available in a migration CFC
Name
Type
Required
Default
Description
table
string
Yes
The Name of the table to add the column to
columnType
string
Yes
The type of the new column
columnName
string
No
The name of the new column
columnNames
string
No
Modern alias for columnName (matches the plural form every TableDefinition column helper accepts). Pass one or the other — not both.
afterColumn
string
No
The name of the column which this column should be inserted after
referenceName
string
No
Name for new reference column, see documentation for references function, required if columnType is 'reference'
default
any
No
Default value for this column
allowNull
boolean
No
Whether to allow NULL values
limit
numeric
No
Character or integer size limit for column
precision
numeric
No
precision value for decimal columns, i.e. number of digits the column can hold
scale
numeric
No
scale value for decimal columns, i.e. number of digits that can be placed to the right of the decimal point (must be less than or equal to precision)
// 1. Add a simple string column to an existing table
addColumn(table="members", columnType="string", columnName="status", limit=50);
// 2. Add a boolean column with a default value and no NULLs allowed
addColumn(
table="members",
columnType="boolean",
columnName="isActive",
default=1,
allowNull=false
);
// 3. Add a decimal column with precision and scale (e.g. for a price field)
addColumn(
table="products",
columnType="decimal",
columnName="price",
precision=10,
scale=2,
default=0,
allowNull=false
);
The name of the property you want to add an error on.
message
string
Yes
The error message (such as "Please enter a correct name in the form field" for example).
name
string
No
A name to identify the error by (useful when you need to distinguish one error from another one set on the same object and you don't want to use the error message itself for that).
// 1. Add an error to the `email` property.
this.addError(property="email", message="Sorry, you are not allowed to use that email. Try again, please.");
// 2. Add a named error so you can distinguish it from other errors on the same property.
this.addError(property="email", message="That email address is already taken.", name="emailTaken");
// 3. Check for the named error after adding it.
this.addError(property="username", message="Username is reserved.", name="reservedUsername");
if (this.hasErrors(property="username", name="reservedUsername")) {
writeOutput("A reserved-username error is present.");
}
Adds an error on the object as a whole (not tied to any specific property).
Name
Type
Required
Default
Description
message
string
Yes
The error message (such as "Please enter a correct name in the form field" for example).
name
string
No
A name to identify the error by (useful when you need to distinguish one error from another one set on the same object and you don't want to use the error message itself for that).
// 1. Add a general error on the object (not tied to any single property)
user = model("User").findByKey(params.userId);
user.addErrorToBase(message="Your account has been locked. Please contact support.");
// 2. Add a named base error so it can be targeted or cleared later
order = model("Order").findByKey(params.orderId);
order.addErrorToBase(message="This order cannot be placed outside business hours.", name="businessHoursViolation");
if (order.hasErrors(name="businessHoursViolation")) {
// handle the named error
}
// 3. Use addErrorToBase inside a custom validation method on the model
function validate() {
if (this.totalAmount > creditLimit()) {
this.addErrorToBase(message="The total amount exceeds your available credit limit.");
}
}
Add a foreign key constraint to the database, using the reference name that was used to create it
Only available in a migration CFC
Name
Type
Required
Default
Description
table
string
Yes
The table name to perform the operation on
referenceTable
string
Yes
The reference table name to perform the operation on
column
string
No
The column name to perform the operation on
columnName
string
No
Modern alias for column (consistent with the rest of the migrator surface).
referenceColumn
string
Yes
The reference column name to perform the operation on
// 1. Add a foreign key from orders.customerId to customers.id
addForeignKey(
table="orders",
referenceTable="customers",
column="customerId",
referenceColumn="id"
);
// 2. Add a foreign key from comments.postId to posts.id
addForeignKey(
table="comments",
referenceTable="posts",
column="postId",
referenceColumn="id"
);
// 3. Use addForeignKey in a migration's up() and remove it in down()
// In your migration CFC:
//
// public void function up() {
// addForeignKey(
// table="order_items",
// referenceTable="orders",
// column="orderId",
// referenceColumn="id"
// );
// }
//
// public void function down() {
// dropForeignKey(table="order_items", keyName="FK_order_items_orders");
// }
Adds a new MIME type to your Wheels application for use with responding to multiple formats.
Name
Type
Required
Default
Description
extension
string
Yes
File extension to add.
mimeType
string
Yes
Matching MIME type to associate with the file extension.
// 1. Add the `js` format
addFormat(extension="js", mimeType="text/javascript");
// 2. Add the `ppt` and `pptx` formats
addFormat(extension="ppt", mimeType="application/vnd.ms-powerpoint");
addFormat(extension="pptx", mimeType="application/vnd.ms-powerpoint");
// 3. Add a custom `csv` format so controllers can respond with `responds(formats="csv")`
addFormat(extension="csv", mimeType="text/csv");
Add database index on a table column
Only available in a migration CFC
Name
Type
Required
Default
Description
table
string
Yes
The table name to perform the index operation on
columnNames
string
No
One or more column names to index, comma separated
unique
boolean
No
false
If true will create a unique index constraint
indexName
string
No
The name of the index to add: Defaults to table name + underscore + first column name
// 1. Add a basic index on a single column
addIndex(table="users", columnNames="email");
// 2. Add a unique index to enforce uniqueness on a column
addIndex(table="members", columnNames="username", unique=true);
// 3. Add a composite index on multiple columns
addIndex(table="orders", columnNames="customerId,createdAt");
// 4. Add an index with a custom index name
// (defaults to tableName_firstColumnName, e.g. "posts_publishedAt")
addIndex(table="posts", columnNames="publishedAt", indexName="idx_posts_published");
Adds a record to a table
Only available in a migration CFC
Name
Type
Required
Default
Description
table
string
Yes
The table name to add the record to
// 1. Insert a simple seed record into a settings table
addRecord(
table = "settings",
name = "siteName",
value = "My Wheels App"
);
// 2. Insert a record with multiple columns (extra keyword arguments become column/value pairs)
addRecord(
table = "people",
id = 1,
title = "Mr",
firstName = "Bruce",
lastName = "Wayne",
email = "bruce@wayneenterprises.com",
phone = "555-678-9099"
);
// 3. Seed an admin user role during a migration's up() function
component extends="wheels.migrator.Migration" {
function up() {
addRecord(
table = "roles",
id = 1,
name = "admin",
active = true
);
}
function down() {
removeRecord(table = "roles", where = "id = 1");
}
}
Add a foreign key constraint to the database, using the reference name that was used to create it
Only available in a migration CFC
Name
Type
Required
Default
Description
table
string
Yes
The table name to perform the operation on
referenceName
string
No
The reference table name to perform the operation on
columnName
string
No
Alias for referenceName (consistent with the modern migrator surface — columnName / columnNames are accepted alongside the legacy form).
columnNames
string
No
Plural alias for referenceName. When both columnName and columnNames are supplied, columnNames wins.
// 1. Add a foreign key from comments.postId to posts.id using a reference name
// Equivalent to: addForeignKey(table="comments", referenceTable="posts", column="postId", referenceColumn="id")
addReference(table="comments", referenceName="post");
// 2. Add a foreign key from order_items.orderId to orders.id
addReference(table="order_items", referenceName="order");
// 3. Use addReference in a migration's up() and undo it with dropReference() in down()
// In your migration CFC:
//
// public void function up() {
// addReference(table="comments", referenceName="post");
// }
//
// public void function down() {
// dropReference(table="comments", referenceName="post");
// }
Registers method(s) that should be called after a new object is created.
Name
Type
Required
Default
Description
methods
string
No
Method name or list of method names that should be called when this callback event occurs in an object's life cycle (can also be called with the method argument).
// 1. Register a single method to run after an object is created
// In models/User.cfc
component extends="Model" {
function config() {
afterCreate("sendWelcomeEmail");
}
private function sendWelcomeEmail() {
// send email to this.email
}
}
// 2. Register multiple methods by passing a comma-delimited list
afterCreate("updateCache,notifyAdmin");
// 3. Register using the named `methods` argument
afterCreate(methods="syncToExternalApi");
Registers method(s) that should be called after an object is deleted.
Name
Type
Required
Default
Description
methods
string
No
Method name or list of method names that should be called when this callback event occurs in an object's life cycle (can also be called with the method argument).
// 1. Call a single method after an object is deleted
// In models/Order.cfc
afterDelete("notifyWarehouse");
// 2. Call multiple methods after an object is deleted (comma-separated list)
// In models/User.cfc
afterDelete("removeFromSearchIndex,clearCachedData");
// 3. Register several after-delete callbacks individually for clarity
// In models/Article.cfc
afterDelete("logDeletion");
afterDelete("cleanupAttachments");
Registers method(s) that should be called after an existing object has been initialized (which is usually done with the findByKey or findOne method).
Name
Type
Required
Default
Description
methods
string
No
Method name or list of method names that should be called when this callback event occurs in an object's life cycle (can also be called with the method argument).
// 1. Register a single callback method to run after records are fetched
// In models/User.cfc
config() {
afterFind("setFetchedAt");
}
// The callback receives each row's columns as arguments; return the struct to modify the record.
function setFetchedAt() {
arguments.fetchedAt = Now();
return arguments;
}
// 2. Format a column value after a find (works for both query rows and objects)
// In models/Product.cfc
config() {
afterFind("formatPrice");
}
function formatPrice() {
if (StructKeyExists(arguments, "price")) {
arguments.price = DollarFormat(arguments.price);
}
return arguments;
}
// 3. Register multiple callback methods as a comma-delimited list
config() {
afterFind("setFetchedAt,formatPrice");
}
Registers method(s) that should be called after an object has been initialized.
Name
Type
Required
Default
Description
methods
string
No
Method name or list of method names that should be called when this callback event occurs in an object's life cycle (can also be called with the method argument).
// 1. Call a single method after any object is initialized (whether new or fetched from the database)
afterInitialization("fixObj");
// 2. Call multiple methods after initialization by passing a comma-delimited list
afterInitialization("setDefaults,fixObj");
Registers method(s) that should be called after a new object has been initialized (which is usually done with the new method).
Name
Type
Required
Default
Description
methods
string
No
Method name or list of method names that should be called when this callback event occurs in an object's life cycle (can also be called with the method argument).
// 1. Call a single method after a new object is initialized
// In models/User.cfc
component extends="Model" {
function config() {
afterNew("setDefaults");
}
private function setDefaults() {
this.role = "member";
this.active = true;
}
}
// 2. Call multiple methods after a new object is initialized (comma-delimited list)
afterNew("setDefaults,generateToken");
// 3. Use the `method` argument alias instead of `methods`
afterNew(method="setDefaults");
Registers method(s) that should be called after an object is saved.
Name
Type
Required
Default
Description
methods
string
No
Method name or list of method names that should be called when this callback event occurs in an object's life cycle (can also be called with the method argument).
// 1. Register a single callback method to run after an object is saved (both create and update)
// In models/User.cfc
component extends="Model" {
function config() {
afterSave("sendWelcomeEmail");
}
private function sendWelcomeEmail() {
// called automatically each time a User is saved
}
}
// 2. Register multiple callback methods using a comma-delimited list
component extends="Model" {
function config() {
afterSave("updateSearchIndex,notifyAdmins");
}
}
// 3. Register multiple callbacks by calling afterSave() more than once
component extends="Model" {
function config() {
afterSave("updateSearchIndex");
afterSave("notifyAdmins");
}
}
Registers method(s) that should be called after an existing object is updated.
Name
Type
Required
Default
Description
methods
string
No
Method name or list of method names that should be called when this callback event occurs in an object's life cycle (can also be called with the method argument).
// 1. Register a single method to run after an object is updated
// In models/User.cfc
component extends="Model" {
function config() {
afterUpdate("clearCache");
}
private function clearCache() {
// invalidate cached data for this user
}
}
// 2. Register multiple methods by passing a comma-delimited list
afterUpdate("clearCache,notifyAuditLog");
// 3. Register using the named `methods` argument
afterUpdate(methods="syncToSearchIndex");
Registers method(s) that should be called after an object is validated.
Name
Type
Required
Default
Description
methods
string
No
Method name or list of method names that should be called when this callback event occurs in an object's life cycle (can also be called with the method argument).
// 1. Call a single method after an object is validated
afterValidation("fixObj");
// 2. Call multiple methods after an object is validated (comma-delimited list)
afterValidation("sanitizeFields,logValidationResult");
// 3. Use the singular `method` alias for clarity
afterValidation(method="trimWhitespace");
Registers method(s) that should be called after a new object is validated.
Name
Type
Required
Default
Description
methods
string
No
Method name or list of method names that should be called when this callback event occurs in an object's life cycle (can also be called with the method argument).
// 1. Call a single method after a new object is validated on create
component extends="Model" {
function config() {
afterValidationOnCreate("assignDefaults");
}
private function assignDefaults() {
if (!Len(this.role)) {
this.role = "member";
}
}
}
// 2. Call multiple methods after validation on create (comma-delimited list)
component extends="Model" {
function config() {
afterValidationOnCreate("sanitizeFields,logNewRecord");
}
}
// 3. Use the singular `method` alias for clarity
component extends="Model" {
function config() {
afterValidationOnCreate(method="trimWhitespace");
}
}
Registers method(s) that should be called after an existing object is validated.
Name
Type
Required
Default
Description
methods
string
No
Method name or list of method names that should be called when this callback event occurs in an object's life cycle (can also be called with the method argument).
// 1. Call a single method after an existing object is validated on update
// In models/User.cfc:
component extends="Model" {
function config() {
afterValidationOnUpdate("stampUpdatedBy");
}
private function stampUpdatedBy() {
this.updatedBy = request.currentUserId;
}
}
// 2. Register multiple methods to run after an existing object is validated on update
component extends="Model" {
function config() {
afterValidationOnUpdate("normalizeSlug,logValidation");
}
private function normalizeSlug() {
this.slug = LCase(Replace(this.title, " ", "-", "all"));
}
private function logValidation() {
// custom logging logic here
}
}
// 1. Collect all validation errors from associated objects on a post
post = model("Post").findOne(where="id=1", include="comments");
errors = post.allAssociationErrors();
// errors -> array of error structs from associated comments (and their associations, recursively)
// Each struct contains keys: property, message, name
// 2. Use allErrors() with includeAssociations=true instead (preferred shorthand)
// allErrors() calls allAssociationErrors() internally when includeAssociations is true
post = model("Post").findOne(where="id=1", include="comments");
allErrors = post.allErrors(includeAssociations=true);
// allErrors -> combined array of the post's own errors plus all associated errors
// 3. Check if any associated model has errors before saving
order = model("Order").findOne(where="id=1", include="lineItems");
associationErrors = order.allAssociationErrors();
if (arrayLen(associationErrors)) {
writeOutput("One or more line items have validation errors.");
}
Returns a struct detailing all changes that have been made on the object but not yet saved to the database.
// 1. Get an object, change some properties, and inspect all changes before saving
member = model("member").findByKey(params.memberId);
member.firstName = params.newFirstName;
member.email = params.newEmail;
changes = member.allChanges();
// changes -> {
// firstName: { changedFrom: "Jane", changedTo: "Janet" },
// email: { changedFrom: "jane@example.com", changedTo: "janet@example.com" }
// }
// 2. Only call allChanges() when there are changes to process
post = model("post").findByKey(params.id);
post.title = params.title;
post.body = params.body;
if (post.hasChanged()) {
changes = post.allChanges();
for (prop in changes) {
writeOutput("'#prop#' changed from '#changes[prop].changedFrom#' to '#changes[prop].changedTo#'");
}
}
// 3. allChanges() returns an empty struct when nothing has changed
user = model("user").findByKey(params.userId);
changes = user.allChanges();
// changes -> {} (empty struct — no unsaved changes)
Returns an array of all the errors on the object.
It does this by storing instances of models that are associations, and not checking associations of those instances because they have already been checked.
Name
Type
Required
Default
Description
includeAssociations
boolean
No
false
seenErrors
array
No
[runtime expression]
is a private argument not meant to be used by the user, the function uses this to ensure circular dependency avoidance.
// 1. Get all errors on a model object after a failed validation
user = model("User").new(username="", password="");
user.valid();
errors = user.allErrors();
// errors ->
// [
// { message: "Username must not be blank.", name: "", property: "username" },
// { message: "Password must not be blank.", name: "", property: "password" }
// ]
// 2. Check for errors and iterate over them
if (user.hasErrors()) {
for (error in user.allErrors()) {
writeOutput(error.property & ": " & error.message);
}
}
// 3. Include errors from associated models (e.g. a user with associated profile)
user = model("User").findOne(where="id=1", include="profile");
user.valid();
allErrors = user.allErrors(includeAssociations=true);
// allErrors contains errors from both user and its associated profile model
Scope routes under an API path prefix. Shorthand for .group(path="api", name="api", ...). Typically used in combination with version() to organize versioned API endpoints.
Name
Type
Required
Default
Description
path
string
No
api
URL path prefix for the API. Defaults to "api".
name
string
No
api
Name prefix for route names. Defaults to "api".
constraints
struct
No
Variable patterns to apply to all child routes.
callback
any
No
A callback function to define nested routes within this API scope.
<cfscript>
// 1. Basic API scope using the default path and name prefix "api"
mapper()
.api()
// Route name: apiUsers
// Example URL: /api/users
.resources("users")
.end()
.end();
// 2. Combine api() with version() for versioned API endpoints
mapper()
.api()
.version(1)
// Route name: apiV1Users
// Example URL: /api/v1/users
.resources("users")
.end()
.version(2)
// Route name: apiV2Products
// Example URL: /api/v2/products
.resources("products")
.end()
.end()
.end();
// 3. Override the default path and name prefixes
mapper()
.api(path="public-api", name="publicApi")
// Route name: publicApiOrders
// Example URL: /public-api/orders
.resources("orders")
.end()
.end();
// 4. Use api() with a callback to avoid manual .end() calls
mapper()
.api(callback=function(m) {
m.version(number=1, callback=function(m) {
// Route name: apiV1Users
// Example URL: /api/v1/users
m.resources("users");
});
})
.end();
</cfscript>
Returns a struct containing all association definitions for this model.
Each key is the association name, and the value is a struct with association metadata
including type (belongsTo, hasMany, hasOne), modelName, foreignKey, joinKey, and dependent.
// 1. Get all association definitions for a model and inspect them
info = model("post").associationInfo();
// info is a struct where each key is an association name, e.g.:
// info.comments.type -> "hasMany"
// info.comments.modelName -> "Comment"
// info.comments.foreignKey -> "postId"
// info.comments.dependent -> "delete"
// info.author.type -> "belongsTo"
// info.author.modelName -> "Author"
// 2. Check whether a specific association is defined on the model
info = model("user").associationInfo();
if (structKeyExists(info, "profile")) {
writeOutput("User has a profile association of type: " & info.profile.type);
}
// 3. Iterate over all associations to build a summary
info = model("article").associationInfo();
for (assocName in info) {
writeOutput(assocName & " -> " & info[assocName].type & " " & info[assocName].modelName);
}
Returns a list of association names defined on this model.
// 1. Get all association names defined on a model
names = model("User").associationNames();
// names -> "profile,posts,comments"
// 2. Check whether a specific association exists on the model
if (listFindNoCase(model("Post").associationNames(), "comments")) {
// the Post model has a "comments" association
}
// 3. Iterate over each association name
for (assocName in listToArray(model("Order").associationNames())) {
writeOutput(assocName);
}
// 1. Embed the CSRF token in a manually-built form hidden field
token = authenticityToken();
writeOutput('<input type="hidden" name="authenticityToken" value="' & token & '">');
// 2. Pass the token as a request header for an AJAX call (e.g. in a JavaScript data island)
writeOutput('<meta name="csrf-token" content="' & authenticityToken() & '">');
// JavaScript can then read this and send it as the X-CSRF-Token header with each POST request.
// 3. Include the token in a JSON API response body so a client can replay it
tokenValue = authenticityToken();
writeOutput(serializeJSON({authenticityToken = tokenValue}));
Returns a hidden form field containing a new authenticity token.
// 1. Include CSRF token in a plain HTML form that POSTs data
// (use this when you are not using startFormTag())
<form action="#urlFor(route='posts')#" method="post">
#authenticityTokenField()#
<!--- other fields here --->
</form>
// 2. Not needed for GET forms — GET requests are not CSRF-protected
<form action="#urlFor(route='posts')#" method="get">
<!--- no token required --->
</form>
Authorizes the current user for an action on a record by dispatching to the
record's policy (app/policies/Policy.cfc). Throws
Wheels.NotAuthorized (HTTP 403) when the policy denies, and returns the
record unchanged when it allows so the call can be inlined:
function update() {
post = authorize(model("Post").findByKey(params.key));
post.update(params.post);
}
A missing policy class throws Wheels.Policy.NotDefined in development and
testing (loud, Pundit-style, to catch typos) and silently denies in
production — the same environment posture as tableName() (##3079). A
policy class that lacks a method for the action denies.
Name
Type
Required
Default
Description
record
any
Yes
The model instance (or model class / model name string) to authorize against.
action
string
No
The policy method to dispatch. Defaults to the current params.action, resolved at call time.
Whether to link URLs, email addresses or both. Possible values are: all (default), URLs and emailAddresses.
relative
boolean
No
true
Should we auto-link relative urls.
encode
any
No
true
Use this argument to decide whether the output of the function should be encoded in order to prevent Cross Site Scripting (XSS) attacks. Set it to true to encode all relevant output for the specific HTML element in question (e.g. tag content, attribute values, and URLs). For HTML elements that have both tag content and attribute values you can set this argument to attributes to only encode attribute values and not tag content.
// 1. Link both URLs and email addresses (default behavior)
result = autoLink("Download CFWheels from http://cfwheels.org/download");
// result -> "Download CFWheels from <a href="http://cfwheels.org/download">http://cfwheels.org/download</a>"
// 2. Link email addresses only
result = autoLink(text="Email us at info@cfwheels.org or visit http://cfwheels.org", link="emailAddresses");
// result -> "Email us at <a href="mailto:info@cfwheels.org">info@cfwheels.org</a> or visit http://cfwheels.org"
// 3. Link URLs only (skip email addresses)
result = autoLink(text="Visit http://cfwheels.org or contact info@cfwheels.org", link="URLs");
// result -> "Visit <a href="http://cfwheels.org">http://cfwheels.org</a> or contact info@cfwheels.org"
// 4. Disable auto-linking of relative URLs
result = autoLink(text="See /docs/guide for details or http://cfwheels.org for more.", relative=false);
// result -> "See /docs/guide for details or <a href="http://cfwheels.org">http://cfwheels.org</a> for more."
Whether or not to enable default validations for this model.
Name
Type
Required
Default
Description
value
boolean
Yes
Set to true or false.
// 1. Disable automatic validations for this model (useful when automatic validations are enabled globally but you want to opt out for a specific model).
component extends="Model" {
function config() {
automaticValidations(false);
}
}
// 2. Explicitly enable automatic validations for this model (useful when automatic validations are disabled globally but you want to opt in for a specific model).
component extends="Model" {
function config() {
automaticValidations(true);
}
}
Calculates the average value for a given property.
Uses the SQL function AVG.
If no records can be found to perform the calculation on you can use the ifNull argument to decide what should be returned.
Name
Type
Required
Default
Description
property
string
Yes
Name of the property to calculate the average for.
where
string
No
Maps to the WHERE clause of the query (or HAVING when necessary). The following operators are supported: =, !=, <>, <, <=, >, >=, LIKE, NOT LIKE, IN, NOT IN, IS NULL, IS NOT NULL, AND, and OR (note that the key words need to be written in upper case). You can also use parentheses to group statements. Nested queries not allowed. You do not need to specify the table name(s); Wheels will do that for you.
include
string
No
Associations that should be included in the query using INNER or LEFT OUTER joins (which join type that is used depends on how the association has been set up in your model). If all included associations are set on the current model, you can specify them in a list (e.g. department,addresses,emails). You can build more complex include strings by using parentheses when the association is set on an included model, like album(artist(genre)), for example. These complex include strings only work when returnAs is set to query though.
distinct
boolean
No
false
When true, AVG will be performed only on each unique instance of a value, regardless of how many times the value occurs.
parameterize
any
No
true
Set to true to use cfqueryparam on all columns, or pass in a list of property names to use cfqueryparam on those only.
ifNull
any
No
The value returned if no records are found. Common usage is to set this to 0 to make sure a numeric value is always returned instead of a blank string.
includeSoftDeletes
boolean
No
false
Set to true to include soft-deleted records in the queries that this method runs.
group
string
No
Maps to the GROUP BY clause of the query. You do not need to specify the table name(s); Wheels will do that for you.
// 1. Get the average salary for all employees.
avgSalary = model("employee").average("salary");
// 2. Get the average salary for employees in a given department.
avgSalary = model("employee").average(property="salary", where="departmentId=#params.key#");
// 3. Make sure a numeric value is always returned if no records are found.
avgSalary = model("employee").average(property="salary", where="salary BETWEEN #params.min# AND #params.max#", ifNull=0);
// 4. Average only distinct salary values (duplicates excluded).
avgSalary = model("employee").average(property="salary", distinct=true);
// 5. Get the average salary grouped by department (returns a query).
avgByDept = model("employee").average(property="salary", group="departmentId");
Registers method(s) that should be called before a new object is created.
Name
Type
Required
Default
Description
methods
string
No
Method name or list of method names that should be called when this callback event occurs in an object's life cycle (can also be called with the method argument).
// 1. Register a single callback method to run before a new object is created
// Defined inside the model's config() function
beforeCreate("setDefaults");
// 2. Register multiple callback methods to run in sequence before creation
beforeCreate("generateSlug,stampCreatedBy");
// 3. Register callbacks using the named argument
beforeCreate(methods="generateToken,normalizeEmail");
Registers method(s) that should be called before an object is deleted.
Name
Type
Required
Default
Description
methods
string
No
Method name or list of method names that should be called when this callback event occurs in an object's life cycle (can also be called with the method argument).
// 1. Register a single method to run before an object is deleted
// In models/Post.cfc
component extends="Model" {
function config() {
beforeDelete("cleanUpAttachments");
}
private function cleanUpAttachments() {
// Remove associated files from disk before the record is deleted
fileDelete(expandPath("/uploads/#this.id#"));
}
}
// 2. Register multiple methods to run before deletion (comma-delimited list)
// In models/User.cfc
component extends="Model" {
function config() {
beforeDelete("revokeTokens,archiveActivity");
}
private function revokeTokens() {
model("Token").deleteAll(where="userId=#this.id#");
}
private function archiveActivity() {
model("ActivityLog").updateAll(
properties="archivedAt=NOW()",
where="userId=#this.id#"
);
}
}
// 3. Halt deletion by returning false from the callback
// In models/Order.cfc
component extends="Model" {
function config() {
beforeDelete("preventIfShipped");
}
private function preventIfShipped() {
// Returning false cancels the delete operation
if (this.status eq "shipped") {
return false;
}
}
}
Registers method(s) that should be called before an object is saved.
Name
Type
Required
Default
Description
methods
string
No
Method name or list of method names that should be called when this callback event occurs in an object's life cycle (can also be called with the method argument).
// 1. Call a single method before every save (create or update)
// In models/User.cfc
component extends="Model" {
function config() {
beforeSave("normalizeEmail");
}
private function normalizeEmail() {
this.email = LCase(Trim(this.email));
}
}
// 2. Register multiple callback methods as a comma-delimited list
component extends="Model" {
function config() {
beforeSave("stripWhitespace,generateSlug");
}
}
// 3. Use the `method` argument alias to register a single callback
component extends="Model" {
function config() {
beforeSave(method="sanitizeContent");
}
}
Registers method(s) that should be called before an existing object is updated.
Name
Type
Required
Default
Description
methods
string
No
Method name or list of method names that should be called when this callback event occurs in an object's life cycle (can also be called with the method argument).
// 1. Call a single method before every update
// In models/User.cfc
component extends="Model" {
function config() {
beforeUpdate("stampUpdatedBy");
}
private function stampUpdatedBy() {
this.updatedBy = session.userId;
}
}
// 2. Register multiple callback methods as a comma-delimited list
component extends="Model" {
function config() {
beforeUpdate("validateOwnership,recalculateTotals");
}
}
// 3. Use the `method` argument alias to register a single callback
component extends="Model" {
function config() {
beforeUpdate(method="fixObj");
}
}
Registers method(s) that should be called before an object is validated.
Name
Type
Required
Default
Description
methods
string
No
Method name or list of method names that should be called when this callback event occurs in an object's life cycle (can also be called with the method argument).
// 1. Register a single method to run before any validation
// In models/User.cfc
component extends="Model" {
function config() {
beforeValidation("normalizeEmail");
validatesPresenceOf("email");
}
private function normalizeEmail() {
this.email = LCase(Trim(this.email));
}
}
// 2. Register multiple methods to run before validation using a comma-delimited list
// In models/Article.cfc
component extends="Model" {
function config() {
beforeValidation("stripTags,setSlug");
validatesPresenceOf(properties="title,slug");
}
private function stripTags() {
this.title = ReReplace(this.title, "<[^>]*>", "", "all");
}
private function setSlug() {
if (!Len(this.slug)) {
this.slug = LCase(ReReplace(Trim(this.title), "\s+", "-", "all"));
}
}
}
// 3. Register callbacks across multiple calls (they are stacked in order)
// In models/Product.cfc
component extends="Model" {
function config() {
beforeValidation("trimFields");
beforeValidation("setDefaults");
}
private function trimFields() {
this.name = Trim(this.name);
}
private function setDefaults() {
if (!Len(this.status)) {
this.status = "draft";
}
}
}
Registers method(s) that should be called before a new object is validated.
Name
Type
Required
Default
Description
methods
string
No
Method name or list of method names that should be called when this callback event occurs in an object's life cycle (can also be called with the method argument).
// 1. Register a single method to run before a new object is validated
// In models/User.cfc config()
beforeValidationOnCreate("sanitizeEmail");
// 2. Register multiple methods to run before a new object is validated
// In models/Order.cfc config()
beforeValidationOnCreate("setDefaultStatus,generateTrackingNumber");
// 3. Use the `method` argument alias instead of `methods`
// In models/Post.cfc config()
beforeValidationOnCreate(method="normalizeSlug");
Registers method(s) that should be called before an existing object is validated.
Name
Type
Required
Default
Description
methods
string
No
Method name or list of method names that should be called when this callback event occurs in an object's life cycle (can also be called with the method argument).
// 1. Register a single method to run before an existing object is validated on update
// Called inside the model's config() function
beforeValidationOnUpdate("sanitizeFields");
// 2. Register multiple methods as a comma-delimited list
beforeValidationOnUpdate("sanitizeFields,enforceBusinessRules");
// 3. Use the `method` argument alias for a single callback
beforeValidationOnUpdate(method="normalizeEmail");
Sets up a belongsTo association between this model and the specified one.
Use this association when this model contains a foreign key referencing another model.
Name
Type
Required
Default
Description
name
string
Yes
Gives the association a name that you refer to when working with the association (in the include argument to findAll, to name one example).
modelName
string
No
Name of associated model (usually not needed if you follow Wheels conventions because the model name will be deduced from the name argument).
foreignKey
string
No
Foreign key property name (usually not needed if you follow Wheels conventions since the foreign key name will be deduced from the name argument).
joinKey
string
No
Column name to join to if not the primary key (usually not needed if you follow Wheels conventions since the join key will be the table's primary key/keys).
joinType
string
No
inner
Use to set the join type when joining associated tables. Possible values are inner (for INNER JOIN) and outer (for LEFT OUTER JOIN).
polymorphic
boolean
No
false
Set to true to declare a polymorphic belongsTo association. The foreign key defaults to {name}Id and a {name}Type column is used to store the owning model name at runtime.
// 1. Specify that instances of this model belong to an author.
// (The table for this model should have a foreign key column, typically named `authorId`.)
belongsTo("author");
// 2. Override naming conventions by specifying `modelName` and `foreignKey` explicitly.
belongsTo(name="bookWriter", modelName="author", foreignKey="authorId");
// 3. Use a LEFT OUTER JOIN instead of the default INNER JOIN when including this association.
belongsTo(name="category", joinType="outer");
// 4. Declare a polymorphic belongsTo association (e.g. a Comment that can belong to a Post or a Photo).
// Wheels will look for `commentableId` and `commentableType` columns on the comments table.
belongsTo(name="commentable", polymorphic=true);
// 1. Add a single bigInteger column to a new table
t = createTable(name='events');
t.bigInteger(columnNames='externalId');
t.string(columnNames='title', limit=255, allowNull=false);
t.timestamps();
t.create();
// 2. Add multiple bigInteger columns at once
t = createTable(name='analytics');
t.bigInteger(columnNames='pageViews,uniqueVisitors', default=0, allowNull=false);
t.string(columnNames='path', limit=500, allowNull=false);
t.timestamps();
t.create();
// 3. Add a bigInteger column with a limit and default when altering an existing table
t = changeTable(name='orders');
t.bigInteger(columnNames='totalCents', default=0, allowNull=false);
t.change();
// 1. Add a single binary column to a new table
t = createTable(name='attachments');
t.string(columnNames='filename', limit=255, allowNull=false);
t.binary(columnNames='fileData');
t.timestamps();
t.create();
// 2. Add multiple binary columns at once
t = createTable(name='media');
t.string(columnNames='title', limit=255, allowNull=false);
t.binary(columnNames='thumbnail,fullImage', allowNull=false);
t.timestamps();
t.create();
// 3. Add a binary column with a default when altering an existing table
t = changeTable(name='documents');
t.binary(columnNames='rawContent', allowNull=true);
t.change();
// 1. Add a single boolean column to a new table
t = createTable(name='products');
t.string(columnNames='name', limit=255, allowNull=false);
t.boolean(columnNames='isActive', default=1, allowNull=false);
t.timestamps();
t.create();
// 2. Add multiple boolean columns at once
t = createTable(name='users');
t.string(columnNames='email', limit=255, allowNull=false);
t.boolean(columnNames='isAdmin,isVerified,isActive', default=0, allowNull=false);
t.timestamps();
t.create();
// 3. Add a boolean column to an existing table
t = changeTable(name='articles');
t.boolean(columnNames='isPublished', default=0, allowNull=false);
t.change();
Builds and returns a string containing a button form control.
Name
Type
Required
Default
Description
content
string
No
Save changes
Content to display inside the button.
type
string
No
submit
The type for the button: button, reset, or submit.
value
string
No
save
The value of the button when submitted.
image
string
No
File name of the image file to use in the button form control.
prepend
string
No
String to prepend to the form control. Useful to wrap the form control with HTML tags.
append
string
No
String to append to the form control. Useful to wrap the form control with HTML tags.
encode
any
No
true
Use this argument to decide whether the output of the function should be encoded in order to prevent Cross Site Scripting (XSS) attacks. Set it to true to encode all relevant output for the specific HTML element in question (e.g. tag content, attribute values, and URLs). For HTML elements that have both tag content and attribute values you can set this argument to attributes to only encode attribute values and not tag content.
// 1. Basic submit button inside a form
#startFormTag(action="create")#
#buttonTag(content="Save changes", value="save")#
#endFormTag()#
<!--- Produces: <button type="submit" value="save">Save changes</button> --->
// 2. Reset button to clear form fields
#buttonTag(content="Clear form", type="reset")#
<!--- Produces: <button type="reset" value="save">Clear form</button> --->
// 3. Plain button (no form submission) with a CSS class and id
#buttonTag(content="Open dialog", type="button", value="open", class="btn btn-secondary", id="openDialogBtn")#
<!--- Produces: <button type="button" value="open" class="btn btn-secondary" id="openDialogBtn">Open dialog</button> --->
// 4. Submit button wrapped in a paragraph using prepend and append
#buttonTag(content="Submit", value="submit", prepend="<p>", append="</p>")#
<!--- Produces: <p><button type="submit" value="submit">Submit</button></p> --->
// 5. Image button (renders an img tag inside the button element)
#buttonTag(content="", image="submit-icon.png", value="save")#
<!--- Produces: <button type="submit" value="save"><img src="/images/submit-icon.png" alt="Submit Icon" /></button> --->
Creates a form containing a single button that submits to the URL. Note: Pass any additional arguments by prefixing them with "input" like inputClass, inputRel, and inputId, and the generated tag will also include those values as HTML attributes.
The URL is built the same way as the linkTo function.
Name
Type
Required
Default
Description
text
string
No
The text content of the button.
image
string
No
If you want to use an image for the button pass in the link to it here (relative from the images folder).
route
string
No
Name of a route that you have configured in config/routes.cfm.
controller
string
No
Name of the controller to include in the URL.
action
string
No
Name of the action to include in the URL.
key
any
No
Key(s) to include in the URL.
params
string
No
Any additional parameters to be set in the query string (example: wheels=cool&x=y). Please note that Wheels uses the & and = characters to split the parameters and encode them properly for you. However, if you need to pass in & or = as part of the value, then you need to encode them (and only them), example: a=cats%26dogs%3Dtrouble!&b=1.
anchor
string
No
Sets an anchor name to be appended to the path.
method
string
No
The type of method to use in the form tag (delete, get, patch, post, and put are the options).
onlyPath
boolean
No
true
If true, returns only the relative URL (no protocol, host name or port).
host
string
No
Set this to override the current host.
protocol
string
No
Set this to override the current protocol.
port
numeric
No
0
Set this to override the current port number.
encode
any
No
true
Use this argument to decide whether the output of the function should be encoded in order to prevent Cross Site Scripting (XSS) attacks. Set it to true to encode all relevant output for the specific HTML element in question (e.g. tag content, attribute values, and URLs). For HTML elements that have both tag content and attribute values you can set this argument to attributes to only encode attribute values and not tag content.
// 1. Basic button that submits to a controller/action
#buttonTo(text="Delete Account", controller="account", action="delete")#
<!--- Outputs: <form action="/account/delete" method="post"><button type="submit">Delete Account</button></form> --->
// 2. If you're already in the `account` controller, CFWheels will assume the current controller
#buttonTo(text="Delete Account", action="delete")#
<!--- Outputs: <form action="/account/delete" method="post"><button type="submit">Delete Account</button></form> --->
// 3. Use `method` to send a DELETE request (a hidden `_method` field is added automatically)
#buttonTo(text="Remove Post", controller="blog", action="delete", key=99, method="delete")#
<!--- Outputs: <form action="/blog/delete/99" method="post"><input type="hidden" name="_method" value="delete" /><button type="submit">Remove Post</button></form> --->
// 4. Use a named route configured in `config/routes.cfm`
#buttonTo(text="Archive", route="archivePost", postId=12)#
// 5. Show a "please wait" state by disabling the button on click — pass extra attributes to the button using the `input` prefix
#buttonTo(text="Place Order", action="checkout", inputId="checkout-btn", inputClass="btn btn-primary", inputData-disable-with="Processing...")#
// 6. Use an image instead of text for the button
#buttonTo(image="icons/trash.png", action="destroy", key=params.id, method="delete")#
Action(s) to cache. This argument is also aliased as actions.
time
numeric
No
60
Minutes to cache the action(s) for.
static
boolean
No
false
Set to true to tell Wheels that this is a static page and that it can skip running the controller filters (before and after filters set on actions). Please note that the onSessionStart and onRequestStart events still execute though.
appendToKey
string
No
List of variables to be evaluated at runtime and included in the cache key so that content can be cached separately.
// 1. Cache the `termsOfUse` action for the default 60 minutes.
caches("termsOfUse");
// 2. Cache two actions for 30 minutes.
caches(actions="browseByUser,browseByTitle", time=30);
// 3. Cache the `termsOfUse` and `codeOfConduct` actions, including their filters.
caches(actions="termsOfUse,codeOfConduct", static=true);
// 4. Cache content separately based on region.
caches(action="home", appendToKey="request.region");
Returns a struct containing all callback definitions for this model, keyed by callback type
(e.g., beforeSave, afterCreate). Each callback type contains an array of callback method names.
// 1. Inspect all registered callbacks for a model
info = model("Order").callbackInfo();
// info is a struct keyed by callback type, each containing an array of method names:
// {
// beforeSave: ["stampUpdatedAt"],
// afterCreate: ["sendConfirmationEmail"],
// afterSave: ["clearCacheEntries"],
// beforeValidation: [],
// beforeValidationOnCreate: [],
// afterValidation: [],
// afterFind: [],
// ...
// }
// 2. Check whether a specific callback type has any registered methods
info = model("User").callbackInfo();
if (arrayLen(info.beforeDelete)) {
writeOutput("User model has beforeDelete callbacks.");
}
// 3. Loop over all callback types and their methods for debugging
info = model("Post").callbackInfo();
for (callbackType in info) {
for (methodName in info[callbackType]) {
writeOutput(callbackType & ": " & methodName);
}
}
Non-throwing boolean policy check for conditionals and views (views run in
the controller's variables scope, so can() is available in templates
automatically):
##linkTo(text="Edit", route="editPost", key=post.id)##
Returns false (deny) for a guest, for an empty record, and for actions the
policy has no method for. A missing policy class still throws
Wheels.Policy.NotDefined in development/testing so typos fail loud; in
production it returns false.
Name
Type
Required
Default
Description
action
string
Yes
The policy method to check.
record
any
No
The model instance (or model class / model name string) to check against. Empty string denies.
Capitalizes the first character of the supplied string.
Name
Type
Required
Default
Description
text
string
Yes
String to capitalize.
// 1. Capitalize the first character of a sentence
result = capitalize("wheels is a framework");
// result -> "Wheels is a framework"
// 2. Capitalize a lowercase word
result = capitalize("hello");
// result -> "Hello"
// 3. Returns an already-capitalized string unchanged
result = capitalize("CFWheels");
// result -> "CFWheels"
// 1. Alter existing columns on a table (default behavior — modifies columns that already exist)
t = changeTable(name='users');
t.string(columnNames='email', limit=255, allowNull=false);
t.boolean(columnNames='active', default=true, allowNull=false);
t.change();
// 2. Add new columns to an existing table using addColumns=true
t = changeTable(name='products');
t.string(columnNames='sku', limit=100, allowNull=false);
t.decimal(columnNames='discountPrice', precision=10, scale=2, allowNull=true);
t.change(addColumns=true);
// 3. Add a foreign key reference column to an existing table
t = changeTable(name='orders');
t.references(columnNames='customer', allowNull=false);
t.change(addColumns=true);
changes a column definition
Only available in a migration CFC
Name
Type
Required
Default
Description
table
string
Yes
The Name of the table where the column is
columnName
string
No
The name of the column
columnNames
string
No
Modern alias for columnName (matches the plural form every TableDefinition column helper accepts). Pass one or the other — not both.
columnType
string
Yes
The type of the column
afterColumn
string
No
The name of the column which this column should be inserted after
referenceName
string
No
Name for reference column, see documentation for references function, required if columnType is 'reference'
default
any
No
Default value for this column
allowNull
boolean
No
Whether to allow NULL values
limit
numeric
No
Character or integer size limit for column
precision
numeric
No
(For decimal type) the maximum number of digits allow
scale
numeric
No
(For decimal type) the number of digits to the right of the decimal point
addColumns
boolean
No
false
if true, attempts to add columns and database will likely throw an error if column already exists
// 1. Change a string column's length limit
changeColumn(table="members", columnName="status", columnType="string", limit=50);
// 2. Change a column type and set a default value
changeColumn(table="orders", columnName="quantity", columnType="integer", default=1);
// 3. Change a decimal column with precision and scale
changeColumn(table="products", columnName="price", columnType="decimal", precision=10, scale=2, allowNull=false);
// 4. Change a text column and explicitly allow NULL values
changeColumn(table="articles", columnName="summary", columnType="text", allowNull=true);
Returns the previous value of a property that has changed.
Returns an empty string if no previous value exists.
Wheels will keep a note of the previous property value until the object is saved to the database.
Name
Type
Required
Default
Description
property
string
Yes
Name of property to get the previous value for.
// 1. Get the previous value of a changed property
user = model("User").findByKey(params.userId);
user.email = params.newEmail;
// Returns the original email address before it was changed
oldEmail = user.changedFrom("email");
// oldEmail -> "original@example.com"
// 2. Use the dynamic shorthand method (equivalent to the above)
oldEmail = user.emailChangedFrom();
// 3. Check if a property changed before accessing the previous value
user = model("User").findByKey(params.userId);
user.firstName = params.firstName;
if (user.hasChanged("firstName")) {
oldName = user.changedFrom("firstName");
// oldName -> "Jane"
}
// Returns empty string if property has not changed or no previous value exists
Returns a list of the object properties that have been changed but not yet saved to the database.
// 1. Find a member, change some properties, then inspect which ones have changed
member = model("member").findByKey(params.memberId);
member.firstName = params.newFirstName;
member.email = params.newEmail;
changed = member.changedProperties();
// changed -> "firstName,email"
// 2. Only save when there are actually unsaved changes
user = model("User").findByKey(params.userId);
user.lastName = params.lastName;
if (Len(user.changedProperties())) {
user.save();
}
// 3. Use changedProperties() alongside changedFrom() to build an audit log entry
post = model("Post").findByKey(params.postId);
post.title = params.title;
post.body = params.body;
changedList = post.changedProperties();
for (prop in ListToArray(changedList)) {
writeOutput("Property '#prop#' was '#post.changedFrom(prop)#', now '#post[prop]#'.");
}
Creates a table definition object to store modifications to table properties
Only available in a migration CFC
Name
Type
Required
Default
Description
name
string
Yes
Name of the table to set change properties on
// 1. Add a new string column to an existing table
t = changeTable(name="employees");
t.string(columnNames="fullName", default="", allowNull=true, limit=255);
t.change();
// 2. Modify multiple columns at once (change type and nullability)
t = changeTable(name="products");
t.integer(columnNames="stock", default=0, allowNull=false);
t.boolean(columnNames="active", default=true, allowNull=false);
t.change();
// 3. Add a new column using addColumns=true so the migration fails gracefully if the column already exists
t = changeTable(name="orders");
t.datetime(columnNames="shippedAt", allowNull=true);
t.change(addColumns=true);
channelSSETag()
string
controller
Generate a 'script' tag that creates an EventSource for a channel.
Convenience view helper for quickly wiring up SSE in templates.
// 1. Add a single char column to a new table
t = createTable(name='countries');
t.char(columnNames='code', limit=2, allowNull=false);
t.string(columnNames='name', limit=100, allowNull=false);
t.timestamps();
t.create();
// 2. Add multiple char columns at once
t = createTable(name='products');
t.char(columnNames='sku,barcode', limit=12, allowNull=false);
t.string(columnNames='title', limit=255, allowNull=false);
t.timestamps();
t.create();
// 3. Add a char column with a default value when altering an existing table
t = changeTable(name='orders');
t.char(columnNames='statusCode', limit=1, default='N', allowNull=false);
t.change();
Builds and returns a string containing a check box form control based on the supplied name.
Note: Pass any additional arguments like class, rel, and id, and the generated tag will also include those values as HTML attributes.
Name
Type
Required
Default
Description
objectName
any
Yes
The variable name of the object to build the form control for.
property
string
Yes
The name of the property to use in the form control.
association
string
No
The name of the association that the property is located on. Used for building nested forms that work with nested properties. If you are building a form with deep nesting, simply pass in a list to the nested object, and Wheels will figure it out.
position
string
No
The position used when referencing a hasMany relationship in the association argument. Used for building nested forms that work with nested properties. If you are building a form with deep nestings, simply pass in a list of positions, and Wheels will figure it out.
checkedValue
string
No
1
Value of check box in its checked state.
uncheckedValue
string
No
0
The value of the check box when it's on the unchecked state.
label
string
No
useDefaultLabel
The label text to use in the form control.
labelPlacement
string
No
around
Whether to place the label before, after, or wrapped around the form control. Label text placement can be controlled using aroundLeft or aroundRight.
prepend
string
No
String to prepend to the form control. Useful to wrap the form control with HTML tags.
append
string
No
String to append to the form control. Useful to wrap the form control with HTML tags.
prependToLabel
string
No
String to prepend to the form control's label. Useful to wrap the form control with HTML tags.
appendToLabel
string
No
String to append to the form control's label. Useful to wrap the form control with HTML tags.
errorElement
string
No
span
HTML tag to wrap the form control with when the object contains errors.
errorClass
string
No
field-with-errors
The class name of the HTML tag that wraps the form control when there are errors.
encode
any
No
true
Use this argument to decide whether the output of the function should be encoded in order to prevent Cross Site Scripting (XSS) attacks. Set it to true to encode all relevant output for the specific HTML element in question (e.g. tag content, attribute values, and URLs). For HTML elements that have both tag content and attribute values you can set this argument to attributes to only encode attribute values and not tag content.
// 1. Basic check box bound to a boolean model property
#checkBox(objectName="photo", property="isPublic", label="Display this photo publicly.")#
// 2. Custom checked and unchecked values (e.g. "yes" / "no" instead of 1 / 0)
#checkBox(objectName="user", property="agreedToTerms", checkedValue="yes", uncheckedValue="no", label="I agree to the terms of service.")#
// 3. Check boxes for a nested association (photos belonging to a user)
<cfloop from="1" to="#ArrayLen(user.photos)#" index="i">
#checkBox(objectName="user", association="photos", position=i, property="isPublic", label="Make public: #user.photos[i].title#")#
</cfloop>
Builds and returns a string containing a check box form control based on the supplied name.
Note: Pass any additional arguments like class, rel, and id, and the generated tag will also include those values as HTML attributes.
Name
Type
Required
Default
Description
name
string
Yes
Name to populate in tag's name attribute.
checked
boolean
No
false
Whether or not the check box should be checked by default.
value
string
No
1
Value of check box in its checked state.
uncheckedValue
string
No
The value of the check box when it's on the unchecked state.
label
string
No
The label text to use in the form control.
labelPlacement
string
No
around
Whether to place the label before, after, or wrapped around the form control. Label text placement can be controlled using aroundLeft or aroundRight.
prepend
string
No
String to prepend to the form control. Useful to wrap the form control with HTML tags.
append
string
No
String to append to the form control. Useful to wrap the form control with HTML tags.
prependToLabel
string
No
String to prepend to the form control's label. Useful to wrap the form control with HTML tags.
appendToLabel
string
No
String to append to the form control's label. Useful to wrap the form control with HTML tags.
encode
any
No
true
Use this argument to decide whether the output of the function should be encoded in order to prevent Cross Site Scripting (XSS) attacks. Set it to true to encode all relevant output for the specific HTML element in question (e.g. tag content, attribute values, and URLs). For HTML elements that have both tag content and attribute values you can set this argument to attributes to only encode attribute values and not tag content.
// 1. Basic usage with a label and a pre-checked state
<cfoutput>
#checkBoxTag(name="subscribe", value="1", label="Subscribe to our newsletter", checked=true)#
</cfoutput>
// 2. Render an unchecked check box that also submits a value when unchecked
<cfoutput>
#checkBoxTag(name="agreeToTerms", value="1", uncheckedValue="0", label="I agree to the terms")#
</cfoutput>
// 3. Loop over a query to render one check box per option, checking those already selected
// Controller
toppings = model("Topping").findAll(order="name");
selectedIds = "2,5,9"; // e.g. previously saved topping IDs as a comma-delimited list
// View
<cfoutput query="toppings">
#checkBoxTag(
name = "toppingIds",
value = toppings.id,
label = toppings.name,
checked = listFindNoCase(selectedIds, toppings.id) GT 0
)#
</cfoutput>
Returns a comprehensive struct of all model metadata suitable for code generation and introspection tools.
Includes model name, table name, primary keys, properties, associations, validations, enums, scopes, and callbacks.
// 1. Inspect all metadata for the User model
info = model("User").classInfo();
// info.modelName -> "User"
// info.tableName -> "users"
// info.primaryKeys -> "id"
// info.propertyNames -> "id,firstName,lastName,email,createdAt,updatedAt"
// info.properties -> struct of column/type metadata keyed by property name
// info.calculatedProperties -> struct of SQL-expression properties keyed by property name
// info.associations -> struct of association definitions (belongsTo, hasMany, etc.)
// info.validations -> struct keyed by trigger (onSave, onCreate, onUpdate) with arrays of rules
// info.enums -> struct of enum definitions keyed by property name
// info.scopes -> struct of named scope definitions
// info.callbacks -> struct of callback arrays keyed by callback type (beforeSave, afterCreate, etc.)
// info.softDeletion -> false (true when the model has a deletedAt column)
// 2. List all association names and their types
info = model("Article").classInfo();
for (assocName in info.associations) {
assoc = info.associations[assocName];
writeOutput(assocName & " (" & assoc.type & ")");
}
// 3. Check soft-deletion and enumerate registered callbacks
info = model("Post").classInfo();
if (info.softDeletion) {
writeOutput("Post uses soft deletion.");
}
for (callbackType in info.callbacks) {
methods = info.callbacks[callbackType];
writeOutput(callbackType & ": " & arrayToList(methods));
}
// 4. Inspect calculated properties defined on the model
info = model("Order").classInfo();
for (propName in info.calculatedProperties) {
calcProp = info.calculatedProperties[propName];
writeOutput(propName & " => " & calcProp.sql);
}
Clears cached action metadata for current controller.
Name
Type
Required
Default
Description
action
string
No
Optional. A single action or list of actions to clear. If not provided, clears all cached actions of current controller.
// 1. Clear all cached action metadata for the current controller.
clearCachableActions();
// 2. Clear the cached metadata for a single action.
clearCachableActions(action="termsOfUse");
// 3. Clear the cached metadata for a list of specific actions.
clearCachableActions(action="browseByUser,browseByTitle");
Clears all internal knowledge of the current state of the object.
Name
Type
Required
Default
Description
property
string
No
string false Name of property to clear information for.
// 1. Clear change tracking for a single property
// Convert startTime to UTC in an afterFind callback, then tell Wheels to treat
// the converted value as the "original" so it won't be flagged as changed or
// saved unnecessarily.
this.startTime = dateConvert("Local2UTC", this.startTime);
this.clearChangeInformation(property="startTime");
// 2. Clear change tracking for all properties at once
// After manually adjusting values in an afterFind callback, reset Wheels'
// internal state so none of the touched properties appear as dirty.
this.clearChangeInformation();
// 3. Typical afterFind callback usage in a model
// In User.cfc config():
// afterFind("normalizeTimestamps");
// The callback method:
function normalizeTimestamps() {
if (structKeyExists(this, "createdAt")) {
this.createdAt = dateConvert("Local2UTC", this.createdAt);
}
if (structKeyExists(this, "updatedAt")) {
this.updatedAt = dateConvert("Local2UTC", this.updatedAt);
}
// Mark both properties as clean so hasChanged() returns false
// and save() won't push them back to the database.
this.clearChangeInformation(property="createdAt");
this.clearChangeInformation(property="updatedAt");
}
Clears out all errors set on the object or only the ones set for a specific property or name.
Name
Type
Required
Default
Description
property
string
No
Specify a property name here if you want to clear all errors set on that property.
name
string
No
Specify an error name here if you want to clear all errors set with that error name.
// 1. Clear all errors on the object
this.clearErrors();
// 2. Clear all errors set on the `firstName` property
this.clearErrors(property="firstName");
// 3. Clear only errors that were set with a specific error name
this.clearErrors(name="invalidFormat");
// 4. Clear errors on a specific property that were also set with a specific name
this.clearErrors(property="email", name="duplicateEmail");
A collection route doesn't require an id because it acts on a collection of objects.
photos/search is an example of a collection route, because it acts on (and displays) a collection of objects.
Builds and returns a string containing a color picker form control based on the supplied objectName and property.
Note: Pass any additional arguments like class, rel, and id, and the generated tag will also include those values as HTML attributes.
Name
Type
Required
Default
Description
objectName
any
Yes
The variable name of the object to build the form control for.
property
string
Yes
The name of the property to use in the form control.
association
string
No
The name of the association that the property is located on. Used for building nested forms that work with nested properties. If you are building a form with deep nesting, simply pass in a list to the nested object, and Wheels will figure it out.
position
string
No
The position used when referencing a hasMany relationship in the association argument. Used for building nested forms that work with nested properties. If you are building a form with deep nestings, simply pass in a list of positions, and Wheels will figure it out.
label
string
No
useDefaultLabel
The label text to use in the form control.
labelPlacement
string
No
around
Whether to place the label before, after, or wrapped around the form control. Label text placement can be controlled using aroundLeft or aroundRight.
prepend
string
No
String to prepend to the form control. Useful to wrap the form control with HTML tags.
append
string
No
String to append to the form control. Useful to wrap the form control with HTML tags.
prependToLabel
string
No
String to prepend to the form control's label. Useful to wrap the form control with HTML tags.
appendToLabel
string
No
String to append to the form control's label. Useful to wrap the form control with HTML tags.
errorElement
string
No
span
HTML tag to wrap the form control with when the object contains errors.
errorClass
string
No
field-with-errors
The class name of the HTML tag that wraps the form control when there are errors.
encode
any
No
true
Use this argument to decide whether the output of the function should be encoded in order to prevent Cross Site Scripting (XSS) attacks. Set it to true to encode all relevant output for the specific HTML element in question (e.g. tag content, attribute values, and URLs). For HTML elements that have both tag content and attribute values you can set this argument to attributes to only encode attribute values and not tag content.
// 1. Basic color picker bound to a model object property
#colorField(objectName="product", property="themeColor")#
// 2. Color picker with a custom label and a CSS class
#colorField(objectName="user", property="profileColor", label="Profile Color", class="color-picker")#
// 3. Color pickers for nested properties on a hasMany association (e.g., palette swatches)
<cfloop from="1" to="#ArrayLen(design.swatches)#" index="i">
#colorField(objectName="design", association="swatches", position="#i#", property="hexValue", label="Swatch ##i#")#
</cfloop>
Builds and returns a string containing a color picker form control based on the supplied name.
Note: Pass any additional arguments like class, rel, and id, and the generated tag will also include those values as HTML attributes.
Name
Type
Required
Default
Description
name
string
Yes
Name to populate in tag's name attribute.
value
string
No
Value to populate in tag's value attribute.
label
string
No
The label text to use in the form control.
labelPlacement
string
No
around
Whether to place the label before, after, or wrapped around the form control. Label text placement can be controlled using aroundLeft or aroundRight.
prepend
string
No
String to prepend to the form control. Useful to wrap the form control with HTML tags.
append
string
No
String to append to the form control. Useful to wrap the form control with HTML tags.
prependToLabel
string
No
String to prepend to the form control's label. Useful to wrap the form control with HTML tags.
appendToLabel
string
No
String to append to the form control's label. Useful to wrap the form control with HTML tags.
encode
any
No
true
Use this argument to decide whether the output of the function should be encoded in order to prevent Cross Site Scripting (XSS) attacks. Set it to true to encode all relevant output for the specific HTML element in question (e.g. tag content, attribute values, and URLs). For HTML elements that have both tag content and attribute values you can set this argument to attributes to only encode attribute values and not tag content.
// 1. Basic color picker with a default value
#colorFieldTag(name="brandColor", value="##FF5733")#
// 2. Color picker with a label and a CSS class
#colorFieldTag(name="themeColor", value="##336699", label="Theme Color", class="color-input")#
// 3. Color picker with label placement and prepend/append wrappers
#colorFieldTag(name="highlightColor", label="Highlight", labelPlacement="before", prepend="<div class=""field"">", append="</div>")#
// 1. Add a generic string column to a new table
t = createTable(name='articles');
t.column(columnName='title', columnType='string', limit=255, allowNull=false);
t.column(columnName='body', columnType='text');
t.timestamps();
t.create();
// 2. Add a column with a default value and precision/scale (for decimals)
t = createTable(name='products');
t.column(columnName='name', columnType='string', limit=100, allowNull=false);
t.column(columnName='price', columnType='decimal', precision=10, scale=2, default='0.00', allowNull=false);
t.column(columnName='stock', columnType='integer', default='0', allowNull=false);
t.timestamps();
t.create();
// 3. Add a custom column when altering an existing table
t = changeTable(name='users');
t.column(columnName='bio', columnType='text', allowNull=true);
t.change();
Returns a struct with data for the named property.
Name
Type
Required
Default
Description
property
string
Yes
Name of property to inspect.
// 1. Get all column metadata for a property
data = model("User").columnDataForProperty("email");
// Returns a struct like:
// { column: "email", validationtype: "string", label: "Email" }
// 2. Inspect metadata before using it
data = model("Product").columnDataForProperty("price");
if (isStruct(data)) {
writeOutput("Column: " & data.column);
writeOutput("Validation type: " & data.validationtype);
}
// 3. Handle the false return when the property does not exist on the model
data = model("User").columnDataForProperty("nonExistentProp");
if (!isStruct(data)) {
writeOutput("Property not found on this model.");
}
Returns the column name mapped for the named model property.
Name
Type
Required
Default
Description
property
string
Yes
Name of property to inspect.
// 1. Get the column name mapped to a model property
col = model("User").columnForProperty("firstName");
// col -> "first_name"
// 2. Check the column for a property before building a raw SQL fragment
col = model("Order").columnForProperty("placedAt");
if (col != false) {
writeOutput("Column in the database: " & col);
}
// 3. Returns false when the property does not exist on the model
col = model("User").columnForProperty("nonExistentProperty");
// col -> false
Returns a list of column names in the table mapped to this model.
The list is ordered according to the columns' ordinal positions in the database table.
// 1. Get the list of column names for the User model
cols = model("User").columnNames();
// cols -> "id,firstName,lastName,email,createdAt,updatedAt,deletedAt"
// 2. Check whether a specific column exists in the table
if (listFindNoCase(model("User").columnNames(), "email")) {
writeOutput("The users table has an email column.");
}
// 3. Iterate over every column name
for (col in listToArray(model("User").columnNames())) {
writeOutput(col);
}
Returns an array of columns names for the table associated with this class.
Does not include calculated properties that will be generated by the Wheels ORM.
// 1. Get all column names for the User model
cols = model("User").columns();
// cols -> ["id", "firstName", "lastName", "email", "createdAt", "updatedAt", "deletedAt"]
// 2. Check whether a specific column exists in the table
cols = model("User").columns();
if (arrayFindNoCase(cols, "deletedAt")) {
writeOutput("Soft-delete column is present");
}
// 3. Iterate over all columns and output their names
cols = model("User").columns();
for (col in cols) {
writeOutput(col);
}
Pass in another model object to see if the two objects are the same.
Name
Type
Required
Default
Description
object
component
Yes
// 1. Check if two model objects are the same instance
user1 = model("User").findByKey(1);
user2 = model("User").findByKey(1);
user3 = user1;
isSame = user1.compareTo(user2);
// isSame -> false (two separate fetches produce distinct instances)
isSame = user1.compareTo(user3);
// isSame -> true (user3 is the same object reference as user1)
// 2. Guard against processing the same object twice in a loop
users = model("User").findAll(returnAs="objects");
currentUser = model("User").findByKey(session.userId);
for (u in users) {
if (!u.compareTo(currentUser)) {
// process all users except the currently logged-in one
sendNotification(u);
}
}
Used to store a section's output for rendering within a layout.
This content store acts as a stack, so you can store multiple pieces of content for a given section.
Name
Type
Required
Default
Description
position
any
No
last
The position in the section's stack where you want the content placed. Valid values are first, last, or the numeric position.
overwrite
any
No
false
Whether or not to overwrite any of the content. Valid values are false, true, or all.
// 1. Store sidebar content for use in the layout
<cfsavecontent variable="mySidebar">
<nav>Recent Posts</nav>
</cfsavecontent>
<cfset contentFor(sidebar=mySidebar)>
<!--- In your layout, output the stored section --->
<cfoutput>
#includeContent("sidebar")#
#includeContent()#
</cfoutput>
// 2. Push content onto a stack — multiple calls append by default
<cfsavecontent variable="firstScript">
<script src="/js/base.js"></script>
</cfsavecontent>
<cfset contentFor(scripts=firstScript)>
<cfsavecontent variable="secondScript">
<script src="/js/page.js"></script>
</cfsavecontent>
<cfset contentFor(scripts=secondScript)>
<!--- Both scripts are rendered in order --->
<cfoutput>#includeContent("scripts")#</cfoutput>
// 3. Prepend content to an existing section using position="first"
<cfsavecontent variable="criticalScript">
<script src="/js/critical.js"></script>
</cfsavecontent>
<cfset contentFor(position="first", scripts=criticalScript)>
// 4. Overwrite an entire section with overwrite="all"
<cfsavecontent variable="replacementSidebar">
<nav>Admin Sidebar</nav>
</cfsavecontent>
<cfset contentFor(overwrite="all", sidebar=replacementSidebar)>
// 5. Overwrite a specific position in the stack (position=1, overwrite=true)
<cfsavecontent variable="updatedScript">
<script src="/js/updated.js"></script>
</cfsavecontent>
<cfset contentFor(position=1, overwrite=true, scripts=updatedScript)>
Includes content for the body section, which equates to the output generated by the view template run by the request.
// 1. Include the view's generated content inside a layout file
// In `app/views/layout.cfm`, place this where the page body should appear
<!DOCTYPE html>
<html>
<head>
<title>My App</title>
</head>
<body>
#contentForLayout()#
</body>
</html>
// 2. Combine with includeContent to render named sections alongside the body
// In `app/views/layout.cfm`
<html>
<head>
#includeContent("head")#
</head>
<body>
#contentForLayout()#
#includeContent("footer")#
</body>
</html>
Returns the number of rows that match the arguments (or all rows if no arguments are passed in).
Uses the SQL function COUNT.
If no records can be found to perform the calculation on, 0 is returned.
Name
Type
Required
Default
Description
where
string
No
Maps to the WHERE clause of the query (or HAVING when necessary). The following operators are supported: =, !=, <>, <, <=, >, >=, LIKE, NOT LIKE, IN, NOT IN, IS NULL, IS NOT NULL, AND, and OR (note that the key words need to be written in upper case). You can also use parentheses to group statements. Nested queries not allowed. You do not need to specify the table name(s); Wheels will do that for you.
include
string
No
Associations that should be included in the query using INNER or LEFT OUTER joins (which join type that is used depends on how the association has been set up in your model). If all included associations are set on the current model, you can specify them in a list (e.g. department,addresses,emails). You can build more complex include strings by using parentheses when the association is set on an included model, like album(artist(genre)), for example. These complex include strings only work when returnAs is set to query though.
parameterize
any
No
true
Set to true to use cfqueryparam on all columns, or pass in a list of property names to use cfqueryparam on those only.
includeSoftDeletes
boolean
No
false
Set to true to include soft-deleted records in the queries that this method runs.
group
string
No
Maps to the GROUP BY clause of the query. You do not need to specify the table name(s); Wheels will do that for you.
// 1. Count all rows in the authors table
authorCount = model("author").count();
// 2. Count authors whose last name starts with "A"
authorOnACount = model("author").count(where="lastName LIKE 'A%'");
// 3. Count authors who have written books with titles starting with "A" (requires a hasMany association from author to book)
authorWithBooksOnACount = model("author").count(include="books", where="books.title LIKE 'A%'");
// 4. Count posts grouped by status, returning a query with one row per status
statusCounts = model("post").count(group="status");
// statusCounts is a query with columns: count, status
// 5. Count the number of comments on a specific post using a dynamic counter method
// (requires a hasMany association from post to comment)
aPost = model("post").findByKey(params.postId);
commentCount = aPost.commentCount();
Creates a new object, saves it to the database (if the validation permits it), and returns it.
If the validation fails, the unsaved object (with errors added to it) is still returned.
Property names and values can be passed in either using named arguments or as a struct to the properties argument.
Name
Type
Required
Default
Description
properties
struct
No
[runtime expression]
The properties you want to set on the object (can also be passed in as named arguments).
parameterize
any
No
true
Set to true to use cfqueryparam on all columns, or pass in a list of property names to use cfqueryparam on those only.
reload
boolean
No
false
Set to true to force Wheels to query the database even though an identical query for this model may have been run in the same request. (The default in Wheels is to get the second query from the model's request-level cache.)
validate
boolean
No
true
Set to false to skip validations for this operation.
transaction
string
No
[runtime expression]
Set this to commit to update the database, rollback to run all the database queries but not commit them, or none to skip transaction handling altogether.
callbacks
boolean
No
true
Set to false to disable callbacks for this method.
allowExplicitTimestamps
boolean
No
false
Set this to true to allow explicit assignment of createdAt or updatedAt properties
// 1. Create a new author and save it to the database
newAuthor = model("author").create(params.author);
// 2. Create using named arguments
newAuthor = model("author").create(firstName="John", lastName="Doe");
// 3. Merge named arguments with a properties struct
newAuthor = model("author").create(active=1, properties=params.author);
// 4. Skip validation when creating (e.g. for seeding trusted data)
newAuthor = model("author").create(properties=params.author, validate=false);
// 5. Create inside a transaction that is rolled back (useful for dry-run testing)
newAuthor = model("author").create(properties=params.author, transaction="rollback");
// 6. Allow explicit createdAt / updatedAt values when importing legacy data
newAuthor = model("author").create(
firstName="Jane",
lastName="Smith",
createdAt="2020-01-15 08:00:00",
allowExplicitTimestamps=true
);
// 7. Scoped create via a hasOne / hasMany association
// (calls model("order").create(customerId=aCustomer.id, shipping=params.shipping) internally)
aCustomer = model("customer").findByKey(params.customerId);
anOrder = aCustomer.createOrder(shipping=params.shipping);
Creates a migration file. Whilst you can use this in your application, the recommended usage is via either the CLI or the provided GUI interface
Name
Type
Required
Default
Description
migrationName
string
Yes
templateName
string
No
migrationPrefix
string
No
timestamp
// 1. Create a blank migration file (uses timestamp prefix by default)
result = application.wheels.migrator.createMigration("CreateUsersTable");
// result -> "The migration 20240815123045_CreateUsersTable.cfc file was created"
// 2. Create a migration from a built-in template (e.g. create-table)
result = application.wheels.migrator.createMigration(
migrationName="CreatePostsTable",
templateName="create-table"
);
// 3. Create a migration using a sequential numeric prefix instead of a timestamp
result = application.wheels.migrator.createMigration(
migrationName="AddIndexToUsers",
migrationPrefix="numeric"
);
// result -> "The migration 001_AddIndexToUsers.cfc file was created"
Creates a view definition object to store view properties
Only available in a migration CFC
Name
Type
Required
Default
Description
name
string
Yes
Name of the view to change properties on
// 1. Create a simple database view that joins users and roles
v = createView(name="userRoles");
v.selectStatement(sql="SELECT u.id, u.firstName, u.lastName, r.name AS roleName FROM users u INNER JOIN roles r ON u.roleId = r.id");
v.create();
// 2. Create a view for active (non-deleted) users using method chaining
createView(name="activeUsers")
.selectStatement(sql="SELECT id, firstName, lastName, email FROM users WHERE deletedAt IS NULL")
.create();
// 3. Full up/down migration using createView and dropView
component extends="wheels.migrator.Migration" {
function up() {
createView(name="publishedArticles")
.selectStatement(sql="SELECT id, title, body, authorId, publishedAt FROM articles WHERE publishedAt IS NOT NULL")
.create();
}
function down() {
dropView(name="publishedArticles");
}
}
Include this in your layouts' head sections to include meta tags containing the authenticity token for use by JavaScript AJAX requests needing to POST data to your application.
// 1. Include CSRF meta tags in your layout's <head> section
// so that JavaScript AJAX requests can read the token and send it
// as a request header when POSTing data to your application.
<head>
<title>My App</title>
#csrfMetaTags()#
</head>
// 2. Reading the token in JavaScript (e.g. with fetch) using the meta tags above
// The rendered HTML will contain two meta tags like:
// <meta name="csrf-param" content="authenticityToken">
// <meta name="csrf-token" content="<generated-token>">
//
// In your JavaScript you can then do:
// const token = document.querySelector('meta[name="csrf-token"]').getAttribute('content');
// fetch('/posts', { method: 'POST', headers: { 'authenticityToken': token }, body: ... });
Use this method to override the data source connection information for this model.
Name
Type
Required
Default
Description
datasource
string
Yes
The data source name to connect to.
username
string
No
The username for the data source.
password
string
No
The password for the data source.
// 1. Override the data source for a model (basic usage).
// In models/User.cfc
config() {
// Tell Wheels to use the data source named `users_source` instead of
// the default one whenever this model makes SQL calls.
dataSource("users_source");
}
// 2. Override the data source with explicit credentials.
// In models/LegacyOrder.cfc
config() {
dataSource(datasource="legacy_db", username="app_reader", password="s3cr3t");
}
// 1. Add a single date column to a new table
t = createTable(name='events');
t.string(columnNames='title', limit=255, allowNull=false);
t.date(columnNames='eventDate', allowNull=false);
t.timestamps();
t.create();
// 2. Add multiple date columns at once
t = createTable(name='subscriptions');
t.string(columnNames='plan', limit=100, allowNull=false);
t.date(columnNames='startDate,endDate', allowNull=false);
t.timestamps();
t.create();
// 3. Add a date column with a default value to an existing table
t = changeTable(name='users');
t.date(columnNames='birthDate', allowNull=true);
t.change();
Builds and returns a string containing a date field form control based on the supplied objectName and property.
Note: Pass any additional arguments like class, rel, and id, and the generated tag will also include those values as HTML attributes.
Name
Type
Required
Default
Description
objectName
any
Yes
The variable name of the object to build the form control for.
property
string
Yes
The name of the property to use in the form control.
association
string
No
The name of the association that the property is located on. Used for building nested forms that work with nested properties. If you are building a form with deep nesting, simply pass in a list to the nested object, and Wheels will figure it out.
position
string
No
The position used when referencing a hasMany relationship in the association argument. Used for building nested forms that work with nested properties. If you are building a form with deep nestings, simply pass in a list of positions, and Wheels will figure it out.
min
string
No
Minimum allowed date (YYYY-MM-DD format).
max
string
No
Maximum allowed date (YYYY-MM-DD format).
step
string
No
label
string
No
useDefaultLabel
The label text to use in the form control.
labelPlacement
string
No
around
Whether to place the label before, after, or wrapped around the form control. Label text placement can be controlled using aroundLeft or aroundRight.
prepend
string
No
String to prepend to the form control. Useful to wrap the form control with HTML tags.
append
string
No
String to append to the form control. Useful to wrap the form control with HTML tags.
prependToLabel
string
No
String to prepend to the form control's label. Useful to wrap the form control with HTML tags.
appendToLabel
string
No
String to append to the form control's label. Useful to wrap the form control with HTML tags.
errorElement
string
No
span
HTML tag to wrap the form control with when the object contains errors.
errorClass
string
No
field-with-errors
The class name of the HTML tag that wraps the form control when there are errors.
encode
any
No
true
Use this argument to decide whether the output of the function should be encoded in order to prevent Cross Site Scripting (XSS) attacks. Set it to true to encode all relevant output for the specific HTML element in question (e.g. tag content, attribute values, and URLs). For HTML elements that have both tag content and attribute values you can set this argument to attributes to only encode attribute values and not tag content.
// 1. Basic date field bound to a model object property
#dateField(objectName="event", property="startDate")#
// 2. Date field with a custom label, min/max constraints, and a CSS class
#dateField(objectName="event", property="startDate", label="Start Date", min="2024-01-01", max="2024-12-31", class="date-picker")#
// 3. Date fields for nested properties on a hasMany association (e.g., schedule items)
<cfloop from="1" to="#ArrayLen(project.milestones)#" index="i">
#dateField(objectName="project", association="milestones", position="#i#", property="dueDate", label="Due Date ##i#")#
</cfloop>
Builds and returns a string containing a date field form control based on the supplied name.
Note: Pass any additional arguments like class, rel, and id, and the generated tag will also include those values as HTML attributes.
Name
Type
Required
Default
Description
name
string
Yes
Name to populate in tag's name attribute.
value
string
No
Value to populate in tag's value attribute.
min
string
No
Minimum allowed date (YYYY-MM-DD format).
max
string
No
Maximum allowed date (YYYY-MM-DD format).
step
string
No
label
string
No
The label text to use in the form control.
labelPlacement
string
No
around
Whether to place the label before, after, or wrapped around the form control. Label text placement can be controlled using aroundLeft or aroundRight.
prepend
string
No
String to prepend to the form control. Useful to wrap the form control with HTML tags.
append
string
No
String to append to the form control. Useful to wrap the form control with HTML tags.
prependToLabel
string
No
String to prepend to the form control's label. Useful to wrap the form control with HTML tags.
appendToLabel
string
No
String to append to the form control's label. Useful to wrap the form control with HTML tags.
encode
any
No
true
Use this argument to decide whether the output of the function should be encoded in order to prevent Cross Site Scripting (XSS) attacks. Set it to true to encode all relevant output for the specific HTML element in question (e.g. tag content, attribute values, and URLs). For HTML elements that have both tag content and attribute values you can set this argument to attributes to only encode attribute values and not tag content.
// 1. Basic date field with a name and label
#dateFieldTag(name="startDate", label="Start Date")#
// 2. Pre-filled date value with min/max constraints
#dateFieldTag(name="eventDate", value="2024-06-15", min="2024-01-01", max="2024-12-31", label="Event Date")#
// 3. Date field with extra HTML attributes passed through
#dateFieldTag(name="dueDate", label="Due Date", class="form-control", id="due-date-input")#
Builds and returns a string containing three select form controls for month, day, and year based on the supplied objectName and property.
Name
Type
Required
Default
Description
objectName
any
No
The variable name of the object to build the form control for.
property
string
No
The name of the property to use in the form control.
association
string
No
The name of the association that the property is located on. Used for building nested forms that work with nested properties. If you are building a form with deep nesting, simply pass in a list to the nested object, and Wheels will figure it out.
position
string
No
The position used when referencing a hasMany relationship in the association argument. Used for building nested forms that work with nested properties. If you are building a form with deep nestings, simply pass in a list of positions, and Wheels will figure it out.
order
string
No
month,day,year
Use to change the order of or exclude date select tags.
separator
string
No
Use to change the character that is displayed between the date select tags.
startYear
numeric
No
2021
First year in select list.
endYear
numeric
No
2031
Last year in select list.
monthDisplay
string
No
names
Pass in names, numbers, or abbreviations to control display.
Whether to include a blank option in the select form control. Pass true to include a blank line or a string that should represent what display text should appear for the empty value (for example, "- Select One -").
label
string
No
false
The label text to use in the form control.
labelPlacement
string
No
around
Whether to place the label before, after, or wrapped around the form control. Label text placement can be controlled using aroundLeft or aroundRight.
prepend
string
No
String to prepend to the form control. Useful to wrap the form control with HTML tags.
append
string
No
String to append to the form control. Useful to wrap the form control with HTML tags.
prependToLabel
string
No
String to prepend to the form control's label. Useful to wrap the form control with HTML tags.
appendToLabel
string
No
String to append to the form control's label. Useful to wrap the form control with HTML tags.
errorElement
string
No
span
HTML tag to wrap the form control with when the object contains errors.
errorClass
string
No
field-with-errors
The class name of the HTML tag that wraps the form control when there are errors.
combine
boolean
No
Set to false to not combine the select parts into a single DateTime object.
encode
any
No
true
Use this argument to decide whether the output of the function should be encoded in order to prevent Cross Site Scripting (XSS) attacks. Set it to true to encode all relevant output for the specific HTML element in question (e.g. tag content, attribute values, and URLs). For HTML elements that have both tag content and attribute values you can set this argument to attributes to only encode attribute values and not tag content.
<!--- Basic date select bound to a model object --->
#dateSelect(objectName="user", property="dateOfBirth")#
<!--- Show fields to select only month and year --->
#dateSelect(objectName="order", property="expirationDate", order="month,year")#
<!--- Display month as numbers and include a blank option --->
#dateSelect(objectName="event", property="startDate", monthDisplay="numbers", includeBlank=true)#
<!--- Restrict year range and use abbreviated month names --->
#dateSelect(objectName="reservation", property="checkInDate", startYear=2024, endYear=2030, monthDisplay="abbreviations")#
Whether to include a blank option in the select form control. Pass true to include a blank line or a string that should represent what display text should appear for the empty value (for example, "- Select One -").
label
string
No
The label text to use in the form control.
labelPlacement
string
No
around
Whether to place the label before, after, or wrapped around the form control. Label text placement can be controlled using aroundLeft or aroundRight.
prepend
string
No
String to prepend to the form control. Useful to wrap the form control with HTML tags.
append
string
No
String to append to the form control. Useful to wrap the form control with HTML tags.
prependToLabel
string
No
String to prepend to the form control's label. Useful to wrap the form control with HTML tags.
appendToLabel
string
No
String to append to the form control's label. Useful to wrap the form control with HTML tags.
combine
boolean
No
Set to false to not combine the select parts into a single DateTime object.
encode
any
No
true
Use this argument to decide whether the output of the function should be encoded in order to prevent Cross Site Scripting (XSS) attacks. Set it to true to encode all relevant output for the specific HTML element in question (e.g. tag content, attribute values, and URLs). For HTML elements that have both tag content and attribute values you can set this argument to attributes to only encode attribute values and not tag content.
// 1. Basic date selection - the "Tag" version accepts `name` and `selected` directly instead of binding to a model object
#dateSelectTags(name="dateStart", selected=params.dateStart)#
// 2. Show only month and year fields (omit day)
#dateSelectTags(name="expiration", selected=params.expiration, order="month,year")#
// 3. Custom year range with a blank option and a label
#dateSelectTags(name="birthdate", selected=params.birthdate, startYear=1920, endYear=2024, includeBlank=true, label="Date of Birth")#
// 1. Add a single datetime column to a new table
t = createTable(name='orders');
t.string(columnNames='status', limit=50, allowNull=false);
t.datetime(columnNames='placedAt', allowNull=false);
t.timestamps();
t.create();
// 2. Add multiple datetime columns at once
t = createTable(name='appointments');
t.string(columnNames='title', limit=255, allowNull=false);
t.datetime(columnNames='startAt,endAt', allowNull=false);
t.timestamps();
t.create();
// 3. Add a nullable datetime column with a default to an existing table
t = changeTable(name='posts');
t.datetime(columnNames='publishedAt', allowNull=true, default='NOW()');
t.change();
Builds and returns a string containing six select form controls (three for date selection and the remaining three for time selection) based on the supplied objectName and property.
Name
Type
Required
Default
Description
objectName
string
Yes
The variable name of the object to build the form control for.
property
string
Yes
The name of the property to use in the form control.
association
string
No
The name of the association that the property is located on. Used for building nested forms that work with nested properties. If you are building a form with deep nesting, simply pass in a list to the nested object, and Wheels will figure it out.
position
string
No
The position used when referencing a hasMany relationship in the association argument. Used for building nested forms that work with nested properties. If you are building a form with deep nestings, simply pass in a list of positions, and Wheels will figure it out.
dateOrder
string
No
month,day,year
Use to change the order of or exclude date select tags.
dateSeparator
string
No
Use to change the character that is displayed between the date select tags.
startYear
numeric
No
2021
First year in select list.
endYear
numeric
No
2031
Last year in select list.
monthDisplay
string
No
names
Pass in names, numbers, or abbreviations to control display.
Use to change the order of or exclude time select tags.
timeSeparator
string
No
:
Use to change the character that is displayed between the time select tags.
minuteStep
numeric
No
1
Pass in 10 to only show minute 10, 20, 30, etc.
secondStep
numeric
No
1
Pass in 10 to only show seconds 10, 20, 30, etc
separator
string
No
-
Use to change the character that is displayed between the first and second set of select tags.
includeBlank
any
No
false
Whether to include a blank option in the select form control. Pass true to include a blank line or a string that should represent what display text should appear for the empty value (for example, "- Select One -").
label
string
No
false
The label text to use in the form control.
labelPlacement
string
No
around
Whether to place the label before, after, or wrapped around the form control. Label text placement can be controlled using aroundLeft or aroundRight.
prepend
string
No
String to prepend to the form control. Useful to wrap the form control with HTML tags.
append
string
No
String to append to the form control. Useful to wrap the form control with HTML tags.
prependToLabel
string
No
String to prepend to the form control's label. Useful to wrap the form control with HTML tags.
appendToLabel
string
No
String to append to the form control's label. Useful to wrap the form control with HTML tags.
errorElement
string
No
span
HTML tag to wrap the form control with when the object contains errors.
errorClass
string
No
field-with-errors
The class name of the HTML tag that wraps the form control when there are errors.
combine
boolean
No
Set to false to not combine the select parts into a single DateTime object.
twelveHour
boolean
No
false
Whether to display the hours in 24 or 12 hour format. 12 hour format has AM/PM drop downs
encode
any
No
true
Use this argument to decide whether the output of the function should be encoded in order to prevent Cross Site Scripting (XSS) attacks. Set it to true to encode all relevant output for the specific HTML element in question (e.g. tag content, attribute values, and URLs). For HTML elements that have both tag content and attribute values you can set this argument to attributes to only encode attribute values and not tag content.
// 1. Basic date and time select for an article's published date
#dateTimeSelect(objectName="article", property="publishedAt")#
// 2. Show only month, day, hour, and minute (exclude year and second)
#dateTimeSelect(objectName="appointment", property="dateTimeStart", dateOrder="month,day", timeOrder="hour,minute")#
// 3. Use 12-hour time format with a blank option and a custom year range
#dateTimeSelect(objectName="event", property="startsAt", twelveHour=true, includeBlank=true, startYear=2020, endYear=2030)#
// 4. Display month as numbers, step minutes by 15, and add a label
#dateTimeSelect(objectName="meeting", property="scheduledAt", monthDisplay="numbers", minuteStep=15, label="Scheduled Date & Time")#
Use to change the order of or exclude time select tags.
timeSeparator
string
No
:
Use to change the character that is displayed between the time select tags.
minuteStep
numeric
No
1
Pass in 10 to only show minute 10, 20, 30, etc.
secondStep
numeric
No
1
Pass in 10 to only show seconds 10, 20, 30, etc.
separator
string
No
-
Use to change the character that is displayed between the first and second set of select tags.
includeBlank
any
No
false
Whether to include a blank option in the select form control. Pass true to include a blank line or a string that should represent what display text should appear for the empty value (for example, "- Select One -").
label
string
No
The label text to use in the form control.
labelPlacement
string
No
around
Whether to place the label before, after, or wrapped around the form control. Label text placement can be controlled using aroundLeft or aroundRight.
prepend
string
No
String to prepend to the form control. Useful to wrap the form control with HTML tags.
append
string
No
String to append to the form control. Useful to wrap the form control with HTML tags.
prependToLabel
string
No
String to prepend to the form control's label. Useful to wrap the form control with HTML tags.
appendToLabel
string
No
String to append to the form control's label. Useful to wrap the form control with HTML tags.
combine
boolean
No
Set to false to not combine the select parts into a single DateTime object.
twelveHour
boolean
No
false
whether to display the hours in 24 or 12 hour format. 12 hour format has AM/PM drop downs
encode
any
No
true
Use this argument to decide whether the output of the function should be encoded in order to prevent Cross Site Scripting (XSS) attacks. Set it to true to encode all relevant output for the specific HTML element in question (e.g. tag content, attribute values, and URLs). For HTML elements that have both tag content and attribute values you can set this argument to attributes to only encode attribute values and not tag content.
// 1. Basic usage - the "Tag" version accepts `name` and `selected` directly instead of binding to a model object
#dateTimeSelectTags(name="dateTimeStart", selected=params.dateTimeStart)#
// 2. Show only month/day for the date portion and hour/minute for the time portion
#dateTimeSelectTags(name="dateTimeStart", selected=params.dateTimeStart, dateOrder="month,day", timeOrder="hour,minute")#
// 3. Custom year range, 15-minute steps, 12-hour format, and a label
#dateTimeSelectTags(name="scheduledAt", selected=params.scheduledAt, startYear=2020, endYear=2030, minuteStep=15, twelveHour=true, label="Scheduled Date & Time")#
Builds and returns a string containing a select form control for the days of the month based on the supplied name.
Name
Type
Required
Default
Description
name
string
Yes
Name to populate in tag's name attribute.
selected
string
No
The day that should be selected initially.
includeBlank
any
No
false
Whether to include a blank option in the select form control. Pass true to include a blank line or a string that should represent what display text should appear for the empty value (for example, "- Select One -").
label
string
No
The label text to use in the form control.
labelPlacement
string
No
around
Whether to place the label before, after, or wrapped around the form control. Label text placement can be controlled using aroundLeft or aroundRight.
prepend
string
No
String to prepend to the form control. Useful to wrap the form control with HTML tags.
append
string
No
String to append to the form control. Useful to wrap the form control with HTML tags.
prependToLabel
string
No
String to prepend to the form control's label. Useful to wrap the form control with HTML tags.
appendToLabel
string
No
String to append to the form control's label. Useful to wrap the form control with HTML tags.
encode
any
No
true
Use this argument to decide whether the output of the function should be encoded in order to prevent Cross Site Scripting (XSS) attacks. Set it to true to encode all relevant output for the specific HTML element in question (e.g. tag content, attribute values, and URLs). For HTML elements that have both tag content and attribute values you can set this argument to attributes to only encode attribute values and not tag content.
// 1. Basic usage: render a day-of-month select for a standalone form (not bound to a model)
#daySelectTag(name="day", selected=params.day)#
// 2. Pre-select day 15 and include a blank prompt at the top
#daySelectTag(name="birthDay", selected=15, includeBlank="-- Select Day --")#
// 3. Wrap the control with a label and surrounding HTML via prepend/append
#daySelectTag(name="day", selected=params.day, label="Day", prepend="<div class=""field"">", append="</div>")#
// 1. Add a single decimal column to a new table
t = createTable(name='products');
t.string(columnNames='name', limit=255, allowNull=false);
t.decimal(columnNames='price', precision=10, scale=2, allowNull=false);
t.timestamps();
t.create();
// 2. Add multiple decimal columns at once
t = createTable(name='measurements');
t.string(columnNames='label', limit=100, allowNull=false);
t.decimal(columnNames='length,width,height', precision=8, scale=4, allowNull=false);
t.timestamps();
t.create();
// 3. Add a nullable decimal column with a default to an existing table
t = changeTable(name='orders');
t.decimal(columnNames='discount', precision=5, scale=2, allowNull=true, default='0.00');
t.change();
Create a route that matches a URL requiring an HTTP DELETE method. We recommend using this matcher to expose actions that delete database records.
Name
Type
Required
Default
Description
name
string
No
Camel-case name of route to reference when build links and form actions (e.g., blogPost).
pattern
string
No
Overrides the URL pattern that will match the route. The default value is a dasherized version of name (e.g., a name of blogPost generates a pattern of blog-post).
to
string
No
Set controller##action combination to map the route to. You may use either this argument or a combination of controller and action.
controller
string
No
Map the route to a given controller. This must be passed along with the action argument.
action
string
No
Map the route to a given action within the controller. This must be passed along with the controller argument.
package
string
No
Indicates a subfolder that the controller will be referenced from (but not added to the URL pattern). For example, if you set this to admin, the controller will be located at admin/YourController.cfc, but the URL path will not contain admin/.
on
string
No
If this route is within a nested resource, you can set this argument to member or collection. A member route contains a reference to the resource's key, while a collection route does not.
redirect
string
No
Redirect via 302 to this URL when this route is matched. Has precedence over controller/action. Use either an absolute link like /about/, or a full canonical link.
Deletes the object, which means the row is deleted from the database (unless prevented by a beforeDelete callback).
Returns true on successful deletion of the row, false otherwise.
Name
Type
Required
Default
Description
parameterize
any
No
true
Set to true to use cfqueryparam on all columns, or pass in a list of property names to use cfqueryparam on those only.
transaction
string
No
[runtime expression]
Set this to commit to update the database, rollback to run all the database queries but not commit them, or none to skip transaction handling altogether.
callbacks
boolean
No
true
Set to false to disable callbacks for this method.
includeSoftDeletes
boolean
No
false
Set to true to include soft-deleted records in the queries that this method runs.
softDelete
boolean
No
true
Set to false to permanently delete a record, even if it has a soft delete column.
// 1. Get a post object and then delete it from the database.
post = model("Post").findByKey(33);
post.delete();
// 2. Permanently delete a record even if the model uses soft deletes.
user = model("User").findByKey(params.userId);
user.delete(softDelete=false);
// 3. Delete a record without running callbacks (e.g. skip `beforeDelete` / `afterDelete`).
comment = model("Comment").findByKey(params.commentId);
comment.delete(callbacks=false);
// 4. If you have a `hasMany` association setup from `post` to `comment`, you can do a scoped call. (The `deleteComment` method below will call `comment.delete()` internally.)
post = model("Post").findByKey(params.postId);
comment = model("Comment").findByKey(params.commentId);
post.deleteComment(comment);
Deletes all records that match the where argument.
By default, objects will not be instantiated and therefore callbacks and validations are not invoked.
You can change this behavior by passing in instantiate=true.
Returns the number of records that were deleted.
Name
Type
Required
Default
Description
where
string
No
Maps to the WHERE clause of the query (or HAVING when necessary). The following operators are supported: =, !=, <>, <, <=, >, >=, LIKE, NOT LIKE, IN, NOT IN, IS NULL, IS NOT NULL, AND, and OR (note that the key words need to be written in upper case). You can also use parentheses to group statements. Nested queries not allowed. You do not need to specify the table name(s); Wheels will do that for you.
include
string
No
Associations that should be included in the query using INNER or LEFT OUTER joins (which join type that is used depends on how the association has been set up in your model). If all included associations are set on the current model, you can specify them in a list (e.g. department,addresses,emails). You can build more complex include strings by using parentheses when the association is set on an included model, like album(artist(genre)), for example. These complex include strings only work when returnAs is set to query though.
reload
boolean
No
false
Set to true to force Wheels to query the database even though an identical query for this model may have been run in the same request. (The default in Wheels is to get the second query from the model's request-level cache.)
parameterize
any
No
true
Set to true to use cfqueryparam on all columns, or pass in a list of property names to use cfqueryparam on those only.
instantiate
boolean
No
false
Whether or not to instantiate the object(s) first. When objects are not instantiated, any callbacks and validations set on them will be skipped.
useIndex
struct
No
[runtime expression]
If you want to specify table index hints, pass in a structure of index names using your model names as the structure keys. Eg: {user="idx_users", post="idx_posts"}. This feature is only supported by MySQL and SQL Server.
transaction
string
No
[runtime expression]
Set this to commit to update the database, rollback to run all the database queries but not commit them, or none to skip transaction handling altogether.
callbacks
boolean
No
true
Set to false to disable callbacks for this method.
includeSoftDeletes
boolean
No
false
Set to true to include soft-deleted records in the queries that this method runs.
softDelete
boolean
No
true
Set to false to permanently delete a record, even if it has a soft delete column.
// 1. Delete all inactive users without instantiating them (skips callbacks and validations).
recordsDeleted = model("User").deleteAll(where="inactive=1");
// 2. Delete all inactive users and run their beforeDelete / afterDelete callbacks.
recordsDeleted = model("User").deleteAll(where="inactive=1", instantiate=true);
// 3. Permanently delete soft-deleted records (bypass the soft-delete column).
recordsDeleted = model("User").deleteAll(where="inactive=1", softDelete=false);
// 4. If you have a `hasMany` association from `Post` to `Comment`, you can use a scoped call. (The `deleteAllComments` method below calls `model("Comment").deleteAll(where="postId=#post.id#")` internally.)
post = model("Post").findByKey(params.postId);
howManyDeleted = post.deleteAllComments();
Finds the record with the supplied key and deletes it.
Returns true on successful deletion of the row, false otherwise.
Name
Type
Required
Default
Description
key
any
Yes
Primary key value(s) of the record to fetch. Separate with comma if passing in multiple primary key values. Accepts a string, list, or a numeric value.
reload
boolean
No
false
Set to true to force Wheels to query the database even though an identical query for this model may have been run in the same request. (The default in Wheels is to get the second query from the model's request-level cache.)
transaction
string
No
[runtime expression]
Set this to commit to update the database, rollback to run all the database queries but not commit them, or none to skip transaction handling altogether.
callbacks
boolean
No
true
Set to false to disable callbacks for this method.
includeSoftDeletes
boolean
No
false
Set to true to include soft-deleted records in the queries that this method runs.
softDelete
boolean
No
true
Set to false to permanently delete a record, even if it has a soft delete column.
// 1. Delete the user with the primary key value of 1.
result = model("User").deleteByKey(1);
// result -> true (if the row was deleted), false otherwise
// 2. Permanently delete a soft-deletable record (bypass soft delete).
result = model("User").deleteByKey(key=1, softDelete=false);
// 3. Delete using a composite primary key (comma-separated values).
result = model("OrderItem").deleteByKey("42,7");
// 4. Delete within a transaction that can be rolled back (useful for testing).
result = model("User").deleteByKey(key=1, transaction="rollback");
Gets an object based on conditions and deletes it.
Name
Type
Required
Default
Description
where
string
No
Maps to the WHERE clause of the query (or HAVING when necessary). The following operators are supported: =, !=, <>, <, <=, >, >=, LIKE, NOT LIKE, IN, NOT IN, IS NULL, IS NOT NULL, AND, and OR (note that the key words need to be written in upper case). You can also use parentheses to group statements. Nested queries not allowed. You do not need to specify the table name(s); Wheels will do that for you.
order
string
No
Maps to the ORDER BY clause of the query. You do not need to specify the table name(s); Wheels will do that for you.
reload
boolean
No
false
Set to true to force Wheels to query the database even though an identical query for this model may have been run in the same request. (The default in Wheels is to get the second query from the model's request-level cache.)
transaction
string
No
[runtime expression]
Set this to commit to update the database, rollback to run all the database queries but not commit them, or none to skip transaction handling altogether.
callbacks
boolean
No
true
Set to false to disable callbacks for this method.
includeSoftDeletes
boolean
No
false
Set to true to include soft-deleted records in the queries that this method runs.
useIndex
struct
No
[runtime expression]
If you want to specify table index hints, pass in a structure of index names using your model names as the structure keys. Eg: {user="idx_users", post="idx_posts"}. This feature is only supported by MySQL and SQL Server.
softDelete
boolean
No
true
Set to false to permanently delete a record, even if it has a soft delete column.
// 1. Delete the user who signed up most recently.
result = model("User").deleteOne(order="signupDate DESC");
// 2. Delete the oldest unpaid invoice for a given customer.
result = model("Invoice").deleteOne(
where="customerId=#params.customerId# AND status='unpaid'",
order="createdAt ASC"
);
// 3. Permanently delete a soft-deletable record (bypass soft-delete behaviour).
result = model("User").deleteOne(
where="status='banned'",
order="createdAt ASC",
softDelete=false
);
// 4. If you have a `hasOne` association set up from `User` to `Profile` you can do a scoped call.
// The `deleteProfile` method will call `model("Profile").deleteOne(where="userId=#aUser.id#")` internally.
aUser = model("User").findByKey(params.userId);
aUser.deleteProfile();
// 1. Deobfuscate a URL parameter to get the original numeric ID
// (Wheels automatically obfuscates numeric URL params when obfuscateUrls is enabled)
originalId = deobfuscateParam("b7ab9a50");
// originalId -> "35"
// 2. Round-trip: obfuscate a value, then deobfuscate it back
obfuscated = obfuscateParam("100");
original = deobfuscateParam(obfuscated);
// original -> "100"
// 3. Non-obfuscated values (e.g. already plain integers) are returned as-is
passthrough = deobfuscateParam("42");
// passthrough -> "42"
Returns a comprehensive health report on the migrator state. Pure
read — no mutation. Used by wheels migrate doctor to surface
orphans, gaps, and pending migrations in one pass.
Result struct:
- healthy: boolean — true iff no orphans AND no pending
- currentVersion: string — highest applied version (may be orphan)
- orphans: array — DB versions with no matching file
- pending: array — local files not yet applied
- summary: struct with .total, .applied, .pending, .orphan counts
- message: human-readable one-paragraph summary
See issue #2780 / PR #2798 for the orphan detection foundation.
Migrates down: will be executed when migrating your schema backward
Along with up(), these are the two main functions in any migration file
Only available in a migration CFC
// 1. Reverse a table creation by dropping the table
// Called automatically when rolling back this migration
function down() {
var state = {};
transaction {
try {
dropTable("employees");
} catch (any e) {
state.exception = e;
}
if (structKeyExists(state, "exception")) {
transaction action="rollback";
throw(
errorCode = "1",
detail = state.exception.detail,
message = state.exception.message,
type = "any"
);
} else {
transaction action="commit";
}
}
}
// 2. Reverse an addColumn() call by removing the column
function down() {
var state = {};
transaction {
try {
removeColumn(table="users", columnName="biography");
} catch (any e) {
state.exception = e;
}
if (structKeyExists(state, "exception")) {
transaction action="rollback";
throw(
errorCode = "1",
detail = state.exception.detail,
message = state.exception.message,
type = "any"
);
} else {
transaction action="commit";
}
}
}
// 3. Paired up() and down() inside a full migration component
component extends="[extends]" hint="Add status column to orders" {
function up() {
var state = {};
transaction {
try {
addColumn(table="orders", columnType="string", columnName="status", limit=50, default="pending");
} catch (any e) {
state.exception = e;
}
if (structKeyExists(state, "exception")) {
transaction action="rollback";
throw(
errorCode = "1",
detail = state.exception.detail,
message = state.exception.message,
type = "any"
);
} else {
transaction action="commit";
}
}
}
function down() {
var state = {};
transaction {
try {
removeColumn(table="orders", columnName="status");
} catch (any e) {
state.exception = e;
}
if (structKeyExists(state, "exception")) {
transaction action="rollback";
throw(
errorCode = "1",
detail = state.exception.detail,
message = state.exception.message,
type = "any"
);
} else {
transaction action="commit";
}
}
}
}
Drops a foreign key constraint from the database
Only available in a migration CFC
Name
Type
Required
Default
Description
table
string
Yes
The table name to perform the operation on
keyName
string
Yes
the name of the key to drop
// 1. Drop a foreign key constraint by its explicit name
// In the down() function, remove a foreign key added in up()
dropForeignKey(table="orders", keyName="FK_orders_customers");
// 2. Drop a foreign key that was created via addReference()
// addReference() creates keys named FK_<table>_<pluralizedReference>
// So addReference(table="comments", referenceName="post") creates "FK_comments_posts"
dropForeignKey(table="comments", keyName="FK_comments_posts");
// 3. Use inside a migration's down() to reverse an addForeignKey() call
// up() called: addForeignKey(table="profiles", referenceTable="users", column="userId", referenceColumn="id")
// down() reverses it:
dropForeignKey(table="profiles", keyName="FK_profiles_users");
Drop a foreign key constraint from the database, using the reference name that was used to create it
Only available in a migration CFC
Name
Type
Required
Default
Description
table
string
Yes
The table name to perform the operation on
referenceName
string
No
the name of the reference to drop
columnName
string
No
Alias for referenceName (consistent with the modern migrator surface — columnName / columnNames are accepted alongside the legacy form).
columnNames
string
No
Plural alias for referenceName. When both columnName and columnNames are supplied, columnNames wins.
// 1. Drop the foreign key from comments.postId back to posts.id
// Removes the constraint named FK_comments_posts
dropReference(table="comments", referenceName="post");
// 2. Drop a foreign key from order_items back to orders
// Removes the constraint named FK_order_items_orders
dropReference(table="order_items", referenceName="order");
// 3. Use dropReference in the down() of a migration that added a reference in up()
// In your migration CFC:
//
// public void function up() {
// addReference(table="comments", referenceName="post");
// }
//
// public void function down() {
// dropReference(table="comments", referenceName="post");
// }
Drops a table from the database
Only available in a migration CFC
Name
Type
Required
Default
Description
name
string
Yes
Name of the table to drop
// 1. Drop a table in the down() migration (reversing a createTable in up())
component extends="wheels.migrator.Migration" hint="Add products table" {
function up() {
t = createTable(name="products");
t.string(columnNames="name", allowNull=false);
t.decimal(columnNames="price", precision=10, scale=2);
t.boolean(columnNames="active", default=1);
t.timestamps();
t.create();
}
function down() {
dropTable("products");
}
}
// 2. Drop multiple tables in a single down() migration
component extends="wheels.migrator.Migration" hint="Add orders and line items tables" {
function up() {
t = createTable(name="lineItems");
t.integer(columnNames="orderId");
t.integer(columnNames="productId");
t.integer(columnNames="quantity");
t.timestamps();
t.create();
t = createTable(name="orders");
t.integer(columnNames="userId");
t.string(columnNames="status");
t.timestamps();
t.create();
}
function down() {
dropTable("lineItems");
dropTable("orders");
}
}
drops a view from the database
Only available in a migration CFC
Name
Type
Required
Default
Description
name
string
Yes
Name of the view to drop
// 1. Drop a view in the down() migration (reversing a createView in up())
component extends="wheels.migrator.Migration" hint="Add active users view" {
function up() {
createView(name="activeUsers")
.selectStatement(sql="SELECT id, firstName, lastName, email FROM users WHERE deletedAt IS NULL")
.create();
}
function down() {
dropView(name="activeUsers");
}
}
// 2. Drop multiple views in a single down() migration
component extends="wheels.migrator.Migration" hint="Add reporting views" {
function up() {
createView(name="publishedArticles")
.selectStatement(sql="SELECT id, title, authorId, publishedAt FROM articles WHERE publishedAt IS NOT NULL")
.create();
createView(name="activeAuthors")
.selectStatement(sql="SELECT id, firstName, lastName FROM users WHERE deletedAt IS NULL")
.create();
}
function down() {
dropView(name="publishedArticles");
dropView(name="activeAuthors");
}
}
Builds and returns a string containing an email field form control based on the supplied objectName and property.
Note: Pass any additional arguments like class, rel, and id, and the generated tag will also include those values as HTML attributes.
Name
Type
Required
Default
Description
objectName
any
Yes
The variable name of the object to build the form control for.
property
string
Yes
The name of the property to use in the form control.
association
string
No
The name of the association that the property is located on. Used for building nested forms that work with nested properties. If you are building a form with deep nesting, simply pass in a list to the nested object, and Wheels will figure it out.
position
string
No
The position used when referencing a hasMany relationship in the association argument. Used for building nested forms that work with nested properties. If you are building a form with deep nestings, simply pass in a list of positions, and Wheels will figure it out.
label
string
No
useDefaultLabel
The label text to use in the form control.
labelPlacement
string
No
around
Whether to place the label before, after, or wrapped around the form control. Label text placement can be controlled using aroundLeft or aroundRight.
prepend
string
No
String to prepend to the form control. Useful to wrap the form control with HTML tags.
append
string
No
String to append to the form control. Useful to wrap the form control with HTML tags.
prependToLabel
string
No
String to prepend to the form control's label. Useful to wrap the form control with HTML tags.
appendToLabel
string
No
String to append to the form control's label. Useful to wrap the form control with HTML tags.
errorElement
string
No
span
HTML tag to wrap the form control with when the object contains errors.
errorClass
string
No
field-with-errors
The class name of the HTML tag that wraps the form control when there are errors.
encode
any
No
true
Use this argument to decide whether the output of the function should be encoded in order to prevent Cross Site Scripting (XSS) attacks. Set it to true to encode all relevant output for the specific HTML element in question (e.g. tag content, attribute values, and URLs). For HTML elements that have both tag content and attribute values you can set this argument to attributes to only encode attribute values and not tag content.
// 1. Basic email field bound to an object property
#emailField(objectName="user", property="emailAddress")#
// 2. Email field with a custom label and a CSS class
#emailField(label="Email Address", objectName="user", property="emailAddress", class="form-control")#
// 3. Nested email field for a contacts association (hasMany)
<cfloop from="1" to="#ArrayLen(account.contacts)#" index="i">
#emailField(label="Contact Email ##i#", objectName="account", association="contacts", position=i, property="email")#
</cfloop>
Builds and returns a string containing an email field form control based on the supplied name.
Note: Pass any additional arguments like class, rel, and id, and the generated tag will also include those values as HTML attributes.
Name
Type
Required
Default
Description
name
string
Yes
Name to populate in tag's name attribute.
value
string
No
Value to populate in tag's value attribute.
label
string
No
The label text to use in the form control.
labelPlacement
string
No
around
Whether to place the label before, after, or wrapped around the form control. Label text placement can be controlled using aroundLeft or aroundRight.
prepend
string
No
String to prepend to the form control. Useful to wrap the form control with HTML tags.
append
string
No
String to append to the form control. Useful to wrap the form control with HTML tags.
prependToLabel
string
No
String to prepend to the form control's label. Useful to wrap the form control with HTML tags.
appendToLabel
string
No
String to append to the form control's label. Useful to wrap the form control with HTML tags.
encode
any
No
true
Use this argument to decide whether the output of the function should be encoded in order to prevent Cross Site Scripting (XSS) attacks. Set it to true to encode all relevant output for the specific HTML element in question (e.g. tag content, attribute values, and URLs). For HTML elements that have both tag content and attribute values you can set this argument to attributes to only encode attribute values and not tag content.
// 1. Basic email field with a name and pre-filled value
#emailFieldTag(name="userEmail", value="hello@example.com")#
// 2. Email field with a label
#emailFieldTag(label="Email Address", name="userEmail", value=params.userEmail)#
// 3. Email field with a CSS class and placeholder passed as extra HTML attributes
#emailFieldTag(name="contactEmail", value="", class="form-control", placeholder="you@example.com")#
Call this to end a nested routing block or the entire route configuration. This method is chained on a sequence of routing mapper method calls started by mapper().
<cfscript>
mapper()
.namespace("admin")
.resources("products")
.end() // Ends the `namespace` block.
.scope(package="public")
.resources(name="products", nested=true)
.resources("variations")
.end() // Ends the nested `resources` block.
.end() // Ends the `scope` block.
.end(); // Ends the `mapper` block.
</cfscript>
Builds and returns a string containing the closing form tag.
Name
Type
Required
Default
Description
prepend
string
No
String to prepend to the form control. Useful to wrap the form control with HTML tags.
append
string
No
String to append to the form control. Useful to wrap the form control with HTML tags.
encode
any
No
true
Use this argument to decide whether the output of the function should be encoded in order to prevent Cross Site Scripting (XSS) attacks. Set it to true to encode all relevant output for the specific HTML element in question (e.g. tag content, attribute values, and URLs). For HTML elements that have both tag content and attribute values you can set this argument to attributes to only encode attribute values and not tag content.
// 1. Close a form opened with startFormTag
#startFormTag(action="create")#
<!--- your form controls --->
#endFormTag()#
// 2. Append a note after the closing form tag
#startFormTag(action="update", method="post")#
<!--- your form controls --->
#endFormTag(append="<p>All fields are required.</p>")#
// 3. Wrap the closing tag with surrounding markup using prepend and append
#startFormTag(route="userSearch")#
<!--- your form controls --->
#endFormTag(prepend="</div>", append="</section>")#
Maps a property to a set of named values (like Rails enums).
Generates boolean checker methods (is()), scopes for each value,
and validates that the property value is one of the allowed values.
Name
Type
Required
Default
Description
property
string
Yes
The name of the model property to map as an enum.
values
any
Yes
Either a comma-delimited list of string values (e.g. "draft,published,archived") or a struct mapping names to stored values (e.g. {low: 0, medium: 1, high: 2}).
// 1. Map a `status` property to a comma-delimited list of allowed string values.
// Wheels auto-validates that `status` is one of the listed values,
// creates `isDraft()`, `isPublished()`, and `isArchived()` checker methods,
// and registers `draft()`, `published()`, and `archived()` query scopes.
component extends="Model" {
function config() {
enum(property="status", values="draft,published,archived");
}
}
// 2. Map a `priority` property using a struct so that the stored database value
// differs from the human-readable name (0, 1, 2 are stored; low/medium/high are the names).
// Generated methods: `isLow()`, `isMedium()`, `isHigh()`.
// Generated scopes: `model("Task").low()`, `model("Task").medium()`, `model("Task").high()`.
component extends="Model" {
function config() {
enum(property="priority", values={low: 0, medium: 1, high: 2});
}
}
// 3. Using the generated checker methods and scopes at runtime.
// `isPublished()` returns true/false; `published()` scopes a finder to that status.
post = model("Post").findByKey(key=42);
if (post.isPublished()) {
writeOutput("This post is live.");
}
// Find all published posts using the auto-generated scope.
publishedPosts = model("Post").published().findAll();
Returns a struct containing all enum definitions for this model.
Each key is the property name, and the value contains values (name-to-stored-value mapping) and names (list of enum names).
// 1. Inspect all enum definitions on the Order model
info = model("Order").enumInfo();
// info -> {
// status: {
// property: "status",
// names: "draft,published,archived",
// values: {draft: "draft", published: "published", archived: "archived"}
// }
// }
// 2. List all enum-mapped properties
info = model("Order").enumInfo();
for (propName in info) {
writeOutput(propName & ": " & info[propName].names);
}
// status: draft,published,archived
// priority: low,medium,high
// 3. Use enum metadata to build a select list for a form
info = model("Order").enumInfo();
statusEnum = info["status"];
for (enumName in listToArray(statusEnum.names)) {
storedValue = statusEnum.values[enumName];
writeOutput(enumName & " -> " & storedValue);
}
// draft -> draft
// published -> published
// archived -> archived
Returns the value of an environment variable. Checks application.env (loaded from .env files) first, then falls back to system environment variables (server.system.environment). Returns the default if the variable is not found in either location.
named argument default is also accepted for backwards compatibility
with pre-rename callers.
Name
Type
Required
Default
Description
name
string
Yes
The environment variable name to look up.
defaultValue
any
No
Value to return if the variable is not found. The legacy
// 1. Read a required environment variable
dbUrl = env("DATABASE_URL");
// dbUrl -> "postgres://user:pass@localhost/myapp" (or "" if not set)
// 2. Provide a default when the variable may be absent
smtpHost = env("SMTP_HOST", "localhost");
// smtpHost -> "localhost" when SMTP_HOST is not defined in .env or system env
// 3. Guard application startup based on an environment variable
appSecret = env("APP_SECRET_KEY");
if (!Len(appSecret)) {
throw(type="App.ConfigError", message="APP_SECRET_KEY must be set.");
}
Returns the number of errors this object has associated with it.
Specify property or name if you wish to count only specific errors.
Name
Type
Required
Default
Description
property
string
No
Specify a property name here if you want to count only errors set on a specific property.
name
string
No
Specify an error name here if you want to count only errors set with a specific error name.
// 1. Check the total number of errors on an object
if (author.errorCount() GTE 10) {
// Too many errors — bail out early
}
// 2. Check how many errors are associated with a specific property
if (author.errorCount(property="email") gt 0) {
// The email property has at least one error
}
// 3. Count errors that were set with a specific error name
count = author.errorCount(name="invalidFormat");
// count -> 2 (two errors share the "invalidFormat" name)
Returns the error message, if one exists, on the object's property.
If multiple error messages exist, the first one is returned.
Name
Type
Required
Default
Description
objectName
string
Yes
The variable name of the object to display the error message for.
property
string
Yes
The name of the property to display the error message for.
prependText
string
No
String to prepend to the error message.
appendText
string
No
String to append to the error message.
wrapperElement
string
No
span
HTML element to wrap the error message in.
class
string
No
error-message
CSS class to set on the wrapper element.
encode
boolean
No
true
Use this argument to decide whether the output of the function should be encoded in order to prevent Cross Site Scripting (XSS) attacks. Set it to true to encode all relevant output for the specific HTML element in question (e.g. tag content, attribute values, and URLs). For HTML elements that have both tag content and attribute values you can set this argument to attributes to only encode attribute values and not tag content.
// 1. Display the first error message (if any) on the email property
#errorMessageOn(objectName="user", property="email")#
// -> <span class="error-message">Email is not a valid email address.</span>
// -> (empty string when no error exists on that property)
// 2. Prepend and append custom text around the error message
#errorMessageOn(objectName="user", property="username", prependText="Problem:", appendText="Please try again.")#
// -> <span class="error-message">Problem: Username has already been taken. Please try again.</span>
// 3. Wrap the message in a div with a custom CSS class instead of the default span
#errorMessageOn(objectName="post", property="title", wrapperElement="div", class="field-error")#
// -> <div class="field-error">Title can't be blank.</div>
Builds and returns a list (ul tag with a default class of error-messages) containing all the error messages for all the properties of the object.
Returns an empty string if no errors exist.
Name
Type
Required
Default
Description
objectName
string
Yes
The variable name of the object to display error messages for.
class
string
No
error-messages
CSS class to set on the ul element.
showDuplicates
boolean
No
true
Whether or not to show duplicate error messages.
encode
boolean
No
true
Use this argument to decide whether the output of the function should be encoded in order to prevent Cross Site Scripting (XSS) attacks. Set it to true to encode all relevant output for the specific HTML element in question (e.g. tag content, attribute values, and URLs). For HTML elements that have both tag content and attribute values you can set this argument to attributes to only encode attribute values and not tag content.
includeAssociations
boolean
No
true
// 1. Display all error messages for a user object
#errorMessagesFor(objectName="user")#
// -> <ul class="error-messages"><li>Email is not a valid email address.</li><li>Username can't be blank.</li></ul>
// -> (empty string when the object has no errors)
// 2. Use a custom CSS class on the wrapping ul element
#errorMessagesFor(objectName="user", class="form-errors")#
// -> <ul class="form-errors"><li>Email is not a valid email address.</li></ul>
// 3. Suppress duplicate error messages
#errorMessagesFor(objectName="user", showDuplicates=false)#
// -> <ul class="error-messages"><li>can't be blank.</li></ul>
// (only one "can't be blank." entry even if multiple fields have the same message)
// 4. Include errors from associated objects as well
#errorMessagesFor(objectName="order", includeAssociations=true)#
// -> <ul class="error-messages"><li>Name can't be blank.</li><li>Line items quantity must be greater than zero.</li></ul>
Returns an array of all errors associated with the supplied property (and error name if passed in).
Name
Type
Required
Default
Description
property
string
Yes
Specify the property name to return errors for here.
name
string
No
If you want to return only errors on the property set with a specific error name you can specify it here.
// 1. Get all errors associated with the emailAddress property
errors = user.errorsOn("emailAddress");
// errors -> [{property: "emailAddress", message: "is invalid", name: ""}, ...]
// 2. Get only errors on emailAddress that were set with a specific error name
errors = user.errorsOn(property="emailAddress", name="formatCheck");
// errors -> [{property: "emailAddress", message: "must be a valid email", name: "formatCheck"}]
// 3. Check errors on a property and loop over them
errors = user.errorsOn("username");
for (error in errors) {
writeOutput(error.message);
}
Returns an array of all errors associated with the object as a whole (not related to any specific property).
Name
Type
Required
Default
Description
name
string
No
Specify an error name here to only return errors for that error name.
// 1. Get all general (base) errors for a model object after validation
user = model("User").new(params.user);
user.valid();
errors = user.errorsOnBase();
// errors -> [{property: "", message: "Account has been suspended", name: ""}]
// 2. Filter base errors by a specific error name
user.addErrorToBase(message="Account has been suspended", name="suspended");
user.addErrorToBase(message="Please accept the terms", name="terms");
suspendedErrors = user.errorsOnBase(name="suspended");
// suspendedErrors -> [{property: "", message: "Account has been suspended", name: "suspended"}]
// 3. Check for base errors and display them
errors = user.errorsOnBase();
if (arrayLen(errors)) {
for (e in errors) {
writeOutput(e.message);
}
}
Extracts an excerpt from text that matches the first instance of a given phrase.
Name
Type
Required
Default
Description
text
string
Yes
The text to extract an excerpt from.
phrase
string
Yes
The phrase to extract.
radius
numeric
No
100
Number of characters to extract surrounding the phrase.
excerptString
string
No
...
String to replace first and / or last characters with.
// 1. Extract text around a matching phrase with a custom radius
result = excerpt(text="CFWheels is a Rails-like MVC framework for Adobe ColdFusion and Lucee", phrase="framework", radius=5);
// result -> "...MVC framework for Ad..."
// 2. Use the default radius of 100 characters
result = excerpt(text="CFWheels is a powerful MVC framework built for ColdFusion developers who want to move fast.", phrase="powerful");
// result -> "CFWheels is a powerful MVC framework built for ColdFusion developers who want to move fast."
// 3. Customize the excerpt string used to indicate truncated text
result = excerpt(text="The quick brown fox jumps over the lazy dog", phrase="fox", radius=5, excerptString=" [...]");
// result -> " [...]brown fox jumps [...]"
Executes a raw sql query
Only available in a migration CFC
Name
Type
Required
Default
Description
sql
string
Yes
Arbitrary SQL String
// 1. Run a raw SQL statement during a migration
execute(sql = "UPDATE users SET active = 1 WHERE active IS NULL");
// 2. Create a database view with raw SQL
execute(sql = "CREATE VIEW active_users AS SELECT * FROM users WHERE active = 1");
// 3. Use execute() inside up() and down() to apply and reverse a custom SQL change
component extends="wheels.migrator.Migration" {
function up() {
execute(sql = "ALTER TABLE orders ADD COLUMN notes TEXT");
}
function down() {
execute(sql = "ALTER TABLE orders DROP COLUMN notes");
}
}
Checks if a record exists in the table.
You can pass in either a primary key value to the key argument or a string to the where argument.
If you don't pass in either of those, it will simply check if any record exists in the table.
Name
Type
Required
Default
Description
key
any
No
Primary key value(s) of the record. Separate with comma if passing in multiple primary key values. Accepts a string, list, or a numeric value.
where
string
No
Maps to the WHERE clause of the query (or HAVING when necessary). The following operators are supported: =, !=, <>, <, <=, >, >=, LIKE, NOT LIKE, IN, NOT IN, IS NULL, IS NOT NULL, AND, and OR (note that the key words need to be written in upper case). You can also use parentheses to group statements. Nested queries not allowed. You do not need to specify the table name(s); Wheels will do that for you.
reload
boolean
No
false
Set to true to force Wheels to query the database even though an identical query for this model may have been run in the same request. (The default in Wheels is to get the second query from the model's request-level cache.)
parameterize
any
No
true
Set to true to use cfqueryparam on all columns, or pass in a list of property names to use cfqueryparam on those only.
includeSoftDeletes
boolean
No
Set to true to include soft-deleted records in the queries that this method runs.
// 1. Check if any record exists in the table
anyUsers = model("User").exists();
// 2. Check if a record with a specific primary key exists
result = model("User").exists(key=params.key);
// 3. Check if a record matching a WHERE condition exists
joeExists = model("User").exists(where="firstName = 'Joe'");
// 4. Use the result in a conditional
if (model("User").exists(key=params.userId)) {
// record was found, proceed
}
// 5. Include soft-deleted records in the check
deletedExists = model("User").exists(where="email='old@example.com'", includeSoftDeletes=true);
// 6. If you have a `belongsTo` association from `Comment` to `Post`, you can do a scoped call. (The `hasPost` method below calls `model("Post").exists(comment.postId)` internally.)
comment = model("Comment").findByKey(params.commentId);
commentHasAPost = comment.hasPost();
// 7. If you have a `hasOne` association from `User` to `Profile`, you can do a scoped call. (The `hasProfile` method below calls `model("Profile").exists(where="userId=#user.id#")` internally.)
user = model("User").findByKey(params.userId);
userHasProfile = user.hasProfile();
// 8. If you have a `hasMany` association from `Post` to `Comment`, you can do a scoped call. (The `hasComments` method below calls `model("Comment").exists(where="postId=#post.id#")` internally.)
post = model("Post").findByKey(params.postId);
postHasComments = post.hasComments();
Builds and returns a string containing a file field form control based on the supplied objectName and property.
Note: Pass any additional arguments like class, rel, and id, and the generated tag will also include those values as HTML attributes.
Name
Type
Required
Default
Description
objectName
any
Yes
The variable name of the object to build the form control for.
property
string
Yes
The name of the property to use in the form control.
association
string
No
The name of the association that the property is located on. Used for building nested forms that work with nested properties. If you are building a form with deep nesting, simply pass in a list to the nested object, and Wheels will figure it out.
position
string
No
The position used when referencing a hasMany relationship in the association argument. Used for building nested forms that work with nested properties. If you are building a form with deep nestings, simply pass in a list of positions, and Wheels will figure it out.
label
string
No
useDefaultLabel
The label text to use in the form control.
labelPlacement
string
No
around
Whether to place the label before, after, or wrapped around the form control. Label text placement can be controlled using aroundLeft or aroundRight.
prepend
string
No
String to prepend to the form control. Useful to wrap the form control with HTML tags.
append
string
No
String to append to the form control. Useful to wrap the form control with HTML tags.
prependToLabel
string
No
String to prepend to the form control's label. Useful to wrap the form control with HTML tags.
appendToLabel
string
No
String to append to the form control's label. Useful to wrap the form control with HTML tags.
errorElement
string
No
span
HTML tag to wrap the form control with when the object contains errors.
errorClass
string
No
field-with-errors
The class name of the HTML tag that wraps the form control when there are errors.
encode
any
No
true
Use this argument to decide whether the output of the function should be encoded in order to prevent Cross Site Scripting (XSS) attacks. Set it to true to encode all relevant output for the specific HTML element in question (e.g. tag content, attribute values, and URLs). For HTML elements that have both tag content and attribute values you can set this argument to attributes to only encode attribute values and not tag content.
// 1. Basic file upload field bound to an object and property
#fileField(objectName="photo", property="imageFile")#
// 2. Provide a custom label for the file field
#fileField(label="Profile Photo", objectName="user", property="avatar")#
// 3. Display file upload fields for a hasMany association using nested properties
<fieldset>
<legend>Screenshots</legend>
<cfloop from="1" to="#ArrayLen(site.screenshots)#" index="i">
#fileField(label="File ##i#", objectName="site", association="screenshots", position=i, property="file")#
#textField(label="Caption ##i#", objectName="site", association="screenshots", position=i, property="caption")#
</cfloop>
</fieldset>
Builds and returns a string containing a file form control based on the supplied name.
Note: Pass any additional arguments like class, rel, and id, and the generated tag will also include those values as HTML attributes.
Name
Type
Required
Default
Description
name
string
Yes
Name to populate in tag's name attribute.
label
string
No
The label text to use in the form control.
labelPlacement
string
No
around
Whether to place the label before, after, or wrapped around the form control. Label text placement can be controlled using aroundLeft or aroundRight.
prepend
string
No
String to prepend to the form control. Useful to wrap the form control with HTML tags.
append
string
No
String to append to the form control. Useful to wrap the form control with HTML tags.
prependToLabel
string
No
String to prepend to the form control's label. Useful to wrap the form control with HTML tags.
appendToLabel
string
No
String to append to the form control's label. Useful to wrap the form control with HTML tags.
encode
any
No
true
Use this argument to decide whether the output of the function should be encoded in order to prevent Cross Site Scripting (XSS) attacks. Set it to true to encode all relevant output for the specific HTML element in question (e.g. tag content, attribute values, and URLs). For HTML elements that have both tag content and attribute values you can set this argument to attributes to only encode attribute values and not tag content.
// 1. Basic file upload field with a label
#fileFieldTag(name="photo", label="Profile Photo")#
// 2. File upload field with a CSS class and no label
#fileFieldTag(name="attachment", class="upload-input")#
// 3. File upload field with label placement and wrapper HTML
#fileFieldTag(name="resume", label="Upload Resume", labelPlacement="before", prepend="<div class='field'>", append="</div>")#
Returns an array of all the filters set on current controller in the order in which they will be executed.
Name
Type
Required
Default
Description
type
string
No
all
Use this argument to return only before or after filters.
// 1. Get the entire filter chain for the current controller
myFilterChain = filterChain();
// myFilterChain -> array of structs, each with keys: through, type, only, except, arguments
// e.g. [{ through: "checkLogin", type: "before", only: "", except: "" }, ...]
// 2. Get only the before-filters
beforeFilters = filterChain(type="before");
for (f in beforeFilters) {
writeOutput(f.through);
}
// 3. Get only the after-filters
afterFilters = filterChain(type="after");
writeOutput(arrayLen(afterFilters));
Tells Wheels to run a function before an action is run or after an action has been run.
Name
Type
Required
Default
Description
through
string
Yes
Function(s) to execute before or after the action(s).
type
string
No
before
Whether to run the function(s) before or after the action(s).
only
string
No
Pass in a list of action names (or one action name) to tell Wheels that the filter function(s) should only be run on these actions.
except
string
No
Pass in a list of action names (or one action name) to tell Wheels that the filter function(s) should be run on all actions except the specified ones.
placement
string
No
append
Pass in prepend to prepend the function(s) to the filter chain instead of appending.
// 1. Run `restrictAccess` before every action in this controller (declared inside config()).
filters("restrictAccess");
// 2. Run two before-filters on every action except `home` and `login`.
filters(through="isLoggedIn, checkIPAddress", except="home, login");
// 3. Run `auditLog` after only the `create`, `update`, and `delete` actions.
filters(through="auditLog", type="after", only="create, update, delete");
// 4. Prepend a filter so it runs before any already-registered filters.
filters(through="maintenanceCheck", placement="prepend");
// Note: filter functions must be declared as `private` in the controller
// to prevent them from being routed as public actions.
// Example controller setup:
// component extends="Controller" {
// function config() {
// filters("restrictAccess");
// }
// private function restrictAccess() {
// if (!isLoggedIn()) {
// redirectTo(route="login");
// }
// }
// }
Returns records from the database table mapped to this model according to the arguments passed in (use the where argument to decide which records to get, use the order argument to set the order in which those records should be returned, and so on).
The records will be returned as either a cfquery result set, an array of objects, or an array of structs (depending on what the returnAs argument is set to).
Name
Type
Required
Default
Description
where
string
No
Maps to the WHERE clause of the query (or HAVING when necessary). The following operators are supported: =, !=, <>, <, <=, >, >=, LIKE, NOT LIKE, IN, NOT IN, IS NULL, IS NOT NULL, AND, and OR (note that the key words need to be written in upper case). You can also use parentheses to group statements. Nested queries not allowed. You do not need to specify the table name(s); Wheels will do that for you.
order
string
No
Maps to the ORDER BY clause of the query. You do not need to specify the table name(s); Wheels will do that for you.
group
string
No
Maps to the GROUP BY clause of the query. You do not need to specify the table name(s); Wheels will do that for you.
select
string
No
Determines how the SELECT clause for the query used to return data will look. You can pass in a list of the properties (which map to columns) that you want returned from your table(s). If you don't set this argument at all, Wheels will select all properties from your table(s). If you specify a table name (e.g. users.email) or alias a column (e.g. fn AS firstName) in the list, then the entire list will be passed through unchanged and used in the SELECT clause of the query. By default, all column names in tables joined via the include argument will be prepended with the singular version of the included table name.
includeCalculated
string
No
List of calculated property names (declared via property(name="...", sql="...", select=false)) to additively opt into this finder's SELECT clause. Unlike select, this does not replace the default column list — the named calculated properties are merged on top of all default columns, so the rest of the record is still returned. Useful for pulling a select=false computed property back in on a single finder without spelling out every other column. Unknown names throw Wheels.CalculatedPropertyNotFound in development/testing and are ignored in production.
distinct
boolean
No
false
Whether to add the DISTINCT keyword to your SELECT clause. Wheels will, when necessary, add this automatically (when using pagination and a hasMany association is used in the include argument, to name one example).
include
string
No
Associations that should be included in the query using INNER or LEFT OUTER joins (which join type that is used depends on how the association has been set up in your model). If all included associations are set on the current model, you can specify them in a list (e.g. department,addresses,emails). You can build more complex include strings by using parentheses when the association is set on an included model, like album(artist(genre)), for example. These complex include strings only work when returnAs is set to query though.
maxRows
numeric
No
-1
Maximum number of records to retrieve. Passed on to the maxRowscfquery attribute. The default, -1, means that all records will be retrieved.
page
numeric
No
0
If you want to paginate records, you can do so by specifying a page number here. For example, getting records 11-20 would be page number 2 when perPage is kept at the default setting (10 records per page). The default, 0, means that records won't be paginated and that the perPage and count arguments will be ignored.
perPage
numeric
No
10
When using pagination, you can specify how many records you want to fetch per page here. This argument is only used when the page argument has been passed in.
count
numeric
No
0
When using pagination and you know in advance how many records you want to paginate through, you can pass in that value here. Doing so will prevent Wheels from running a COUNT query to get this value. This argument is only used when the page argument has been passed in.
handle
string
No
query
Handle to use for the query. This is used when you're paginating multiple queries and need to reference them individually in the paginationLinks() function. It's also used to set the name of the query in the debug output (which otherwise defaults to userFindAllQuery for example).
cache
any
No
If you want to cache the query, you can do so by specifying the number of minutes you want to cache the query for here. If you set it to true, the default cache time will be used (60 minutes).
reload
boolean
No
false
Set to true to force Wheels to query the database even though an identical query for this model may have been run in the same request. (The default in Wheels is to get the second query from the model's request-level cache.)
parameterize
any
No
true
Set to true to use cfqueryparam on all columns, or pass in a list of property names to use cfqueryparam on those only.
returnAs
string
No
query
Set to objects to return an array of objects, set to structs to return an array of structs, set to query to return a query result set, or set to 'sql' to return the executed SQL query as a string.
returnIncluded
boolean
No
true
When returnAs is set to objects, you can set this argument to false to prevent returning objects fetched from associations specified in the include argument. This is useful when you only need to include associations for use in the WHERE clause only and want to avoid the performance hit that comes with object creation.
callbacks
boolean
No
true
Set to false to disable callbacks for this method.
includeSoftDeletes
boolean
No
false
Set to true to include soft-deleted records in the queries that this method runs.
useIndex
struct
No
[runtime expression]
If you want to specify table index hints, pass in a structure of index names using your model names as the structure keys. Eg: {user="idx_users", post="idx_posts"}. This feature is only supported by MySQL and SQL Server.
dataSource
string
No
[runtime expression]
Override the default datasource
// 1. Get all users ordered by last name
users = model("user").findAll(order="lastName ASC");
// 2. Get only 5 users in a random order
fiveRandomUsers = model("user").findAll(maxRows=5, order="random");
// 3. Include a belongsTo association and filter with a WHERE clause
articles = model("article").findAll(include="author", where="published=1", order="createdAt DESC");
// 4. Include a hasMany association in the opposite direction
bobsArticles = model("author").findAll(include="articles", where="firstName='Bob'");
// 5. Use pagination (records 26-50) with a nested include (song belongsTo album, album belongsTo artist)
songs = model("song").findAll(include="album(artist)", page=2, perPage=25);
// 6. Return results as an array of model objects instead of a query
activeUsers = model("user").findAll(where="active=1", order="lastName ASC", returnAs="objects");
for (user in activeUsers) {
writeOutput(user.firstName & " " & user.lastName);
}
// 7. Return results as an array of structs
userStructs = model("user").findAll(where="active=1", returnAs="structs");
// 8. Use a dynamic finder to get all books released in a certain year
// (same as model("book").findAll(where="releaseYear=#params.year#"))
books = model("book").findAllByReleaseYear(params.year);
// 9. Use a dynamic finder with multiple criteria
// (same as model("book").findAll(where="releaseYear=#params.year# AND type='#params.type#'"))
books = model("book").findAllByReleaseYearAndType("#params.year#,#params.type#");
// 10. Use a scoped call via a hasMany association (calls findAll internally with a where clause)
post = model("post").findByKey(params.postId);
comments = post.comments();
// 11. Use GROUP BY with a calculated property (generates HAVING instead of WHERE)
// Order model has a calculated property: property(name="totalAmount", sql="SUM(amount)")
ids = model("order").findAll(group="productId", where="totalAmount > 1000", select="productId");
// 12. Include soft-deleted records
allUsers = model("user").findAll(includeSoftDeletes=true, order="lastName ASC");
// 13. Cache the query results for 10 minutes
cachedUsers = model("user").findAll(where="active=1", cache=10);
// 14. Return the generated SQL string instead of running the query
sql = model("user").findAll(where="active=1", order="lastName ASC", returnAs="sql");
// sql -> "SELECT ... FROM users WHERE active = 1 ORDER BY last_name ASC"
// 15. Use index hints (MySQL and SQL Server only)
indexes = {
author="idx_authors_name",
post="idx_posts_created"
};
posts = model("author").findAll(
where="firstName LIKE '#params.q#%' OR subject LIKE '#params.q#%'",
include="posts",
useIndex=indexes
);
Returns all primary key values in a list.
In addition to quoted and delimiter you can pass in any argument that findAll() accepts.
Name
Type
Required
Default
Description
quoted
boolean
No
false
Set to true to enclose each value in single-quotation marks.
delimiter
string
No
,
The delimiter character to separate the list items with.
// 1. Get a comma-delimited list of all primary key values for the Artist model
primaryKeyList = model("artist").findAllKeys();
// primaryKeyList -> "1,2,3,4,5"
// 2. Get keys for active artists only, enclosed in single quotes, separated by a pipe
primaryKeyList = model("artist").findAllKeys(quoted=true, delimiter="|", where="active=1");
// primaryKeyList -> "'1'|'3'|'5'"
// 3. Use the result directly in a SQL IN clause for a subsequent query
keyList = model("artist").findAllKeys(quoted=true, where="genreId=7");
albums = model("album").findAll(where="artistId IN (#keyList#)");
Fetches the requested record by primary key and returns it as an object.
Returns false if no record is found.
You can override this behavior to return a cfquery result set instead, similar to what's described in the documentation for findOne().
Name
Type
Required
Default
Description
key
any
Yes
Primary key value(s) of the record. Separate with comma if passing in multiple primary key values. Accepts a string, list, or a numeric value.
select
string
No
Determines how the SELECT clause for the query used to return data will look. You can pass in a list of the properties (which map to columns) that you want returned from your table(s). If you don't set this argument at all, Wheels will select all properties from your table(s). If you specify a table name (e.g. users.email) or alias a column (e.g. fn AS firstName) in the list, then the entire list will be passed through unchanged and used in the SELECT clause of the query. By default, all column names in tables joined via the include argument will be prepended with the singular version of the included table name.
includeCalculated
string
No
List of calculated property names (declared via property(name="...", sql="...", select=false)) to additively opt into this finder's SELECT clause. Unlike select, this does not replace the default column list — the named calculated properties are merged on top of all default columns, so the rest of the record is still returned. Useful for pulling a select=false computed property back in on a single finder without spelling out every other column. Unknown names throw Wheels.CalculatedPropertyNotFound in development/testing and are ignored in production.
include
string
No
Associations that should be included in the query using INNER or LEFT OUTER joins (which join type that is used depends on how the association has been set up in your model). If all included associations are set on the current model, you can specify them in a list (e.g. department,addresses,emails). You can build more complex include strings by using parentheses when the association is set on an included model, like album(artist(genre)), for example. These complex include strings only work when returnAs is set to query though.
handle
string
No
query
Handle to use for the query. This is used to set the name of the query in the debug output (which otherwise defaults to userFindOneQuery for example).
cache
any
No
If you want to cache the query, you can do so by specifying the number of minutes you want to cache the query for here. If you set it to true, the default cache time will be used (60 minutes).
reload
boolean
No
false
Set to true to force Wheels to query the database even though an identical query for this model may have been run in the same request. (The default in Wheels is to get the second query from the model's request-level cache.)
parameterize
any
No
true
Set to true to use cfqueryparam on all columns, or pass in a list of property names to use cfqueryparam on those only.
returnAs
string
No
object
Set to objects to return an array of objects, set to structs to return an array of structs, set to query to return a query result set, or set to 'sql' to return the executed SQL query as a string.
callbacks
boolean
No
true
Set to false to disable callbacks for this method.
includeSoftDeletes
boolean
No
false
Set to true to include soft-deleted records in the queries that this method runs.
dataSource
string
No
[runtime expression]
Override the default datasource
// 1. Get the author with the primary key value `99` as an object
auth = model("author").findByKey(99);
// 2. Get an author based on a form/URL value and handle the not-found case
auth = model("author").findByKey(params.key);
if (!isObject(auth)) {
flashInsert(message="Author #params.key# was not found");
redirectTo(back=true);
}
// 3. Fetch only selected columns for a record
user = model("user").findByKey(key=params.id, select="id,firstName,email");
// 4. Include a belongsTo association when fetching by key
order = model("order").findByKey(key=params.orderId, include="customer");
// 5. Cache the lookup for 10 minutes and include soft-deleted records
product = model("product").findByKey(key=params.id, cache=10, includeSoftDeletes=true);
// 6. Use a scoped call via a belongsTo association (calls `model("post").findByKey(comment.postId)` internally)
comment = model("comment").findByKey(params.commentId);
post = comment.post();
Processes large result sets one record at a time without loading everything into memory.
Internally paginates through records and invokes the callback for each individual record.
The callback receives a model object (when returnAs is "object") or a struct representing one row.
Name
Type
Required
Default
Description
batchSize
numeric
No
1000
Number of records to load per internal database query. Defaults to 1000.
callback
any
Yes
A function/closure to call for each record. Receives a single argument: the record (object or struct).
where
string
No
Maps to the WHERE clause of the query (or HAVING when necessary). The following operators are supported: =, !=, <>, <, <=, >, >=, LIKE, NOT LIKE, IN, NOT IN, IS NULL, IS NOT NULL, AND, and OR (note that the key words need to be written in upper case). You can also use parentheses to group statements. Nested queries not allowed. You do not need to specify the table name(s); Wheels will do that for you.
order
string
No
Maps to the ORDER BY clause of the query. You do not need to specify the table name(s); Wheels will do that for you.
include
string
No
Associations that should be included in the query using INNER or LEFT OUTER joins (which join type that is used depends on how the association has been set up in your model). If all included associations are set on the current model, you can specify them in a list (e.g. department,addresses,emails). You can build more complex include strings by using parentheses when the association is set on an included model, like album(artist(genre)), for example. These complex include strings only work when returnAs is set to query though.
select
string
No
Determines how the SELECT clause for the query used to return data will look. You can pass in a list of the properties (which map to columns) that you want returned from your table(s). If you don't set this argument at all, Wheels will select all properties from your table(s). If you specify a table name (e.g. users.email) or alias a column (e.g. fn AS firstName) in the list, then the entire list will be passed through unchanged and used in the SELECT clause of the query. By default, all column names in tables joined via the include argument will be prepended with the singular version of the included table name.
parameterize
any
No
Set to true to use cfqueryparam on all columns, or pass in a list of property names to use cfqueryparam on those only.
includeSoftDeletes
boolean
No
false
Set to true to include soft-deleted records in the queries that this method runs.
returnAs
string
No
object
Whether each record is an "object" (default) or "struct".
// 1. Send a newsletter email to every subscriber one at a time
model("Subscriber").findEach(callback = function(subscriber) {
sendEmail(
to = subscriber.email,
subject = "Monthly Newsletter",
template = "/emails/newsletter",
subscriber = subscriber
);
});
// 2. Process users as structs and write a report entry for each one
model("User").findEach(
where = "active = 1",
order = "lastName ASC",
returnAs = "struct",
callback = function(user) {
writeLog("Processing user: #user.firstName# #user.lastName#");
}
);
// 3. Archive old orders in smaller batches to reduce memory pressure
model("Order").findEach(
where = "createdAt < '#DateFormat(DateAdd('yyyy', -2, Now()), 'yyyy-mm-dd')#'",
batchSize = 250,
callback = function(order) {
order.archived = true;
order.save();
}
);
Fetches the first record ordered by primary key value.
Use the property argument to order by something else.
Returns a model object.
Name
Type
Required
Default
Description
property
string
No
[runtime expression]
Name of the property to order by. This argument is also aliased as properties.
// 1. Get the first user record (ordered by primary key ascending)
user = model("User").findFirst();
// Returns the model object with the lowest primary key value, or false if no records exist.
// 2. Get the first user ordered by a specific property
user = model("User").findFirst(property="createdAt");
// Returns the oldest user (lowest createdAt value).
// 3. Get the first active user ordered by last name, then first name
user = model("User").findFirst(properties="lastName,firstName", where="active = 1");
// Equivalent to: ORDER BY lastName ASC, firstName ASC with a WHERE clause applied.
// Returns a model object, or false if no matching record is found.
Processes large result sets in batches without loading everything into memory at once.
The callback receives a query result set (or array of objects/structs) for each batch.
Name
Type
Required
Default
Description
batchSize
numeric
No
500
Number of records per batch. Defaults to 500.
callback
any
Yes
A function/closure to call for each batch. Receives a single argument: the batch (query, array of objects, or array of structs depending on returnAs).
where
string
No
Maps to the WHERE clause of the query (or HAVING when necessary). The following operators are supported: =, !=, <>, <, <=, >, >=, LIKE, NOT LIKE, IN, NOT IN, IS NULL, IS NOT NULL, AND, and OR (note that the key words need to be written in upper case). You can also use parentheses to group statements. Nested queries not allowed. You do not need to specify the table name(s); Wheels will do that for you.
order
string
No
Maps to the ORDER BY clause of the query. You do not need to specify the table name(s); Wheels will do that for you.
include
string
No
Associations that should be included in the query using INNER or LEFT OUTER joins (which join type that is used depends on how the association has been set up in your model). If all included associations are set on the current model, you can specify them in a list (e.g. department,addresses,emails). You can build more complex include strings by using parentheses when the association is set on an included model, like album(artist(genre)), for example. These complex include strings only work when returnAs is set to query though.
select
string
No
Determines how the SELECT clause for the query used to return data will look. You can pass in a list of the properties (which map to columns) that you want returned from your table(s). If you don't set this argument at all, Wheels will select all properties from your table(s). If you specify a table name (e.g. users.email) or alias a column (e.g. fn AS firstName) in the list, then the entire list will be passed through unchanged and used in the SELECT clause of the query. By default, all column names in tables joined via the include argument will be prepended with the singular version of the included table name.
parameterize
any
No
Set to true to use cfqueryparam on all columns, or pass in a list of property names to use cfqueryparam on those only.
includeSoftDeletes
boolean
No
false
Set to true to include soft-deleted records in the queries that this method runs.
returnAs
string
No
query
Set to "query" (default), "objects", or "structs" for the batch format.
// 1. Process all users in batches of 500 (default), logging each batch
model("User").findInBatches(callback=function(batch) {
writeOutput("Processing " & batch.recordCount & " users");
});
// 2. Process active orders in batches of 200, filtered with a WHERE clause
model("Order").findInBatches(
batchSize=200,
where="status='active'",
order="createdAt ASC",
callback=function(batch) {
// batch is a cfquery result set by default
for (local.i = 1; local.i <= batch.recordCount; local.i++) {
writeOutput(batch.orderId[local.i] & ": " & batch.total[local.i]);
}
}
);
// 3. Receive each batch as an array of model objects instead of a query
model("User").findInBatches(
batchSize=100,
returnAs="objects",
where="active=1",
callback=function(batch) {
for (user in batch) {
user.sendNewsletter();
}
}
);
// 4. Receive each batch as an array of structs
model("Product").findInBatches(
batchSize=250,
returnAs="structs",
select="id,name,price",
callback=function(batch) {
for (product in batch) {
writeOutput(product.name & " costs " & product.price);
}
}
);
// 5. Include soft-deleted records while processing in batches
model("User").findInBatches(
includeSoftDeletes=true,
callback=function(batch) {
writeOutput("Batch has " & batch.recordCount & " records (including deleted)");
}
);
Fetches the last record ordered by primary key value.
Use the property argument to order by something else.
Returns a model object. Formerly known as findLast.
Name
Type
Required
Default
Description
property
string
No
Name of the property to order by. This argument is also aliased as properties.
// 1. Get the last user record (ordered by primary key descending)
lastUser = model("User").findLastOne();
// Returns a User object for the record with the highest primary key, or false if none exist.
// 2. Get the most recently created post by ordering on a different property
latestPost = model("Post").findLastOne(property="createdAt");
// Returns the Post object with the latest createdAt value.
// 3. Get the last active product, using a where clause alongside the property order
lastActive = model("Product").findLastOne(property="updatedAt", where="isActive = 1");
// Returns the most recently updated active product, or false if none exist.
Fetches the first record found based on the WHERE and ORDER BY clauses.
With the default settings (i.e. the returnAs argument set to object), a model object will be returned if the record is found and the boolean value false if not.
Instead of using the where argument, you can create cleaner code by making use of a concept called Dynamic Finders.
Name
Type
Required
Default
Description
where
string
No
Maps to the WHERE clause of the query (or HAVING when necessary). The following operators are supported: =, !=, <>, <, <=, >, >=, LIKE, NOT LIKE, IN, NOT IN, IS NULL, IS NOT NULL, AND, and OR (note that the key words need to be written in upper case). You can also use parentheses to group statements. Nested queries not allowed. You do not need to specify the table name(s); Wheels will do that for you.
order
string
No
Maps to the ORDER BY clause of the query. You do not need to specify the table name(s); Wheels will do that for you.
select
string
No
Determines how the SELECT clause for the query used to return data will look. You can pass in a list of the properties (which map to columns) that you want returned from your table(s). If you don't set this argument at all, Wheels will select all properties from your table(s). If you specify a table name (e.g. users.email) or alias a column (e.g. fn AS firstName) in the list, then the entire list will be passed through unchanged and used in the SELECT clause of the query. By default, all column names in tables joined via the include argument will be prepended with the singular version of the included table name.
includeCalculated
string
No
List of calculated property names (declared via property(name="...", sql="...", select=false)) to additively opt into this finder's SELECT clause. Unlike select, this does not replace the default column list — the named calculated properties are merged on top of all default columns, so the rest of the record is still returned. Useful for pulling a select=false computed property back in on a single finder without spelling out every other column. Unknown names throw Wheels.CalculatedPropertyNotFound in development/testing and are ignored in production.
include
string
No
Associations that should be included in the query using INNER or LEFT OUTER joins (which join type that is used depends on how the association has been set up in your model). If all included associations are set on the current model, you can specify them in a list (e.g. department,addresses,emails). You can build more complex include strings by using parentheses when the association is set on an included model, like album(artist(genre)), for example. These complex include strings only work when returnAs is set to query though.
handle
string
No
query
Handle to use for the query. This is used to set the name of the query in the debug output (which otherwise defaults to userFindOneQuery for example).
cache
any
No
If you want to cache the query, you can do so by specifying the number of minutes you want to cache the query for here. If you set it to true, the default cache time will be used (60 minutes).
reload
boolean
No
false
Set to true to force Wheels to query the database even though an identical query for this model may have been run in the same request. (The default in Wheels is to get the second query from the model's request-level cache.)
parameterize
any
No
true
Set to true to use cfqueryparam on all columns, or pass in a list of property names to use cfqueryparam on those only.
returnAs
string
No
object
Set to objects to return an array of objects, set to structs to return an array of structs, set to query to return a query result set, or set to 'sql' to return the executed SQL query as a string.
includeSoftDeletes
boolean
No
false
Set to true to include soft-deleted records in the queries that this method runs.
useIndex
struct
No
[runtime expression]
If you want to specify table index hints, pass in a structure of index names using your model names as the structure keys. Eg: {user="idx_users", post="idx_posts"}. This feature is only supported by MySQL and SQL Server.
dataSource
string
No
[runtime expression]
Override the default datasource
// 1. Get the most recent order as an object from the database
order = model("Order").findOne(order="datePurchased DESC");
// 2. Use a where clause to find the first user with a specific email address
user = model("User").findOne(where="email='someone@example.com'");
// 3. Use a dynamic finder to get the first person with the last name Smith.
// Equivalent to: model("User").findOne(where="lastName='Smith'")
person = model("User").findOneByLastName("Smith");
// 4. Use a dynamic finder to match on two columns.
// Equivalent to: model("User").findOne(where="email='someone@example.com' AND password='mypass'")
user = model("User").findOneByEmailAndPassword("someone@example.com", "mypass");
// 5. Return false when no matching record is found (the default returnAs="object" behavior)
user = model("User").findOne(where="email='unknown@example.com'");
if (!isObject(user)) {
writeOutput("No user found.");
}
// 6. Return as a query result set instead of an object
userQuery = model("User").findOne(where="role='admin'", returnAs="query");
// 7. Use a scoped call via a hasOne association from User to Profile.
// The profile() method calls model("Profile").findOne(where="userId=#user.id#") internally.
user = model("User").findByKey(params.userId);
profile = user.profile();
// 8. Use a scoped call via a hasMany association from Post to Comment.
// The findOneComment() method calls model("Comment").findOne(where="postId=#post.id#") internally.
post = model("Post").findByKey(params.postId);
comment = post.findOneComment(where="approved=1");
Creates a link to the first page, or a disabled span when already on the first page.
Name
Type
Required
Default
Description
text
string
No
First
The text for the link.
handle
string
No
query
The handle given to the query that the pagination should be displayed for.
name
string
No
page
The name of the param that holds the current page number.
class
string
No
CSS class for the link element.
disabledClass
string
No
disabled
CSS class for the disabled span element.
showDisabled
boolean
No
true
Whether to render a disabled span when already on the first page.
pageNumberAsParam
boolean
No
true
Decides whether to link the page number as a param or as part of a route.
encode
any
No
true
Use this argument to decide whether the output of the function should be encoded in order to prevent Cross Site Scripting (XSS) attacks. Set it to true to encode all relevant output for the specific HTML element in question (e.g. tag content, attribute values, and URLs). For HTML elements that have both tag content and attribute values you can set this argument to attributes to only encode attribute values and not tag content.
//--------------------------------------------------------------------
// Example 1: Basic usage — show a "First" link at the top of a
// paginated list; renders a disabled span when already on page 1
// Controller code
param name="params.page" type="integer" default="1";
posts = model("Post").findAll(page=params.page, perPage=10, order="createdAt DESC");
// View code
<cfoutput>
#firstPageLink()#
#previousPageLink()#
#pageNumberLinks()#
#nextPageLink()#
#lastPageLink()#
</cfoutput>
//--------------------------------------------------------------------
// Example 2: Custom link text and CSS classes
// View code
<cfoutput>
#firstPageLink(
text="«« First",
class="page-link",
disabledClass="page-link disabled"
)#
</cfoutput>
//--------------------------------------------------------------------
// Example 3: Hide the disabled element entirely when on the first page
// View code
<cfoutput>
#firstPageLink(showDisabled=false)#
</cfoutput>
//--------------------------------------------------------------------
// Example 4: Use a named route so page numbers appear in the URL path
// instead of as a query-string param (e.g. /articles/page/3)
// Route setup in app/config/routes.cfm
mapper()
.get(name="paginatedArticles", pattern="articles/page/[page]", to="articles##index")
.get(name="articles", pattern="articles", to="articles##index")
.end();
// Controller code
param name="params.page" type="integer" default="1";
articles = model("Article").findAll(page=params.page, perPage=20, order="title");
// View code
<cfoutput>
#firstPageLink(route="paginatedArticles", pageNumberAsParam=false)#
</cfoutput>
Returns the value of a specific key in the Flash (or the entire Flash as a struct if no key is passed in).
Name
Type
Required
Default
Description
key
string
No
The key to get the value for.
// 1. Get the current value of a specific key in the Flash
notice = flash("notice");
// notice -> "Your profile was updated successfully."
// 2. Get the entire Flash as a struct when no key is passed
flashContents = flash();
// flashContents -> {notice: "Record saved.", error: "Something went wrong."}
// 3. Check for a key before reading it to avoid an empty-string fallback
if (flashKeyExists("error")) {
errorMessage = flash("error");
}
// 1. Clear all flash data
flashClear();
// 2. Insert some messages, then clear them all before redirecting
flashInsert(notice="Record saved.");
flashInsert(warning="Check your settings.");
// Oops — wipe everything and start fresh
flashClear();
// flash() is now an empty struct: {}
// 3. Clear flash conditionally inside a controller action
function checkout() {
if (!isLoggedIn()) {
flashClear();
flashInsert(error="You must be logged in to check out.");
redirectTo(action="login");
}
}
// 1. Check how many keys are currently in the Flash
count = flashCount();
// count -> 0 (Flash is empty), or a positive integer when keys exist
// 2. Only render a Flash notice section when there is something to show
if (flashCount()) {
writeOutput("You have " & flashCount() & " flash message(s).");
}
// 3. Use alongside flashIsEmpty() — flashCount() powers the isEmpty check
flashInsert(notice="Saved successfully", warning="Check your email");
count = flashCount();
// count -> 2
Deletes a specific key from the Flash.
Returns true if the key exists.
Name
Type
Required
Default
Description
key
string
Yes
The key to delete
// 1. Delete a key from the Flash
flashDelete(key="errorMessage");
// 2. Check the return value — true if the key existed, false if it did not
wasPresent = flashDelete(key="notice");
// wasPresent -> true (key existed and was removed)
// wasPresent -> false (key did not exist in the Flash)
// 3. Conditionally act on whether the key was actually removed
if (flashDelete(key="warning")) {
// key existed; it has now been removed from the Flash
} else {
// key was not present; nothing was changed
}
// 1. Insert a single key / value into the Flash
flashInsert(notice="Your profile has been updated.");
// 2. Insert multiple keys at once
flashInsert(success="Account created.", hint="Check your email to confirm.");
// 3. Read back a Flash value in the next action or view
// (After a redirect, in the destination action or its view:)
msg = flash("notice");
// msg -> "Your profile has been updated."
// 1. Check whether the Flash is empty before rendering a notice area
if (!flashIsEmpty()) {
writeOutput(flash("notice"));
}
// 2. Insert a message and confirm the Flash is no longer empty
flashInsert(notice="Record saved successfully.");
empty = flashIsEmpty();
// empty -> false
// 3. After clearing the Flash, confirm it is empty again
flashClear();
empty = flashIsEmpty();
// empty -> true
Make the entire Flash or specific key in it stick around for one more request.
Name
Type
Required
Default
Description
key
string
No
// 1. Keep the entire Flash for the next request
flashKeep();
// 2. Keep the "error" key in the Flash for the next request
flashKeep("error");
// 3. Keep both the "error" and "success" keys in the Flash for the next request
flashKeep("error,success");
// 1. Check for an "error" key before reading it
if (flashKeyExists("error")) {
errorMessage = flash("error");
}
// 2. Conditionally display a success notice
if (flashKeyExists("success")) {
writeOutput(flash("success"));
}
// 3. Guard before deleting a specific flash key
if (flashKeyExists("notice")) {
flashDelete("notice");
}
Displays a marked-up listing of messages that exist in the Flash.
Name
Type
Required
Default
Description
keys
string
No
The key (or list of keys) to show the value for. You can also use the key argument instead for better readability when accessing a single key.
class
string
No
flash-messages
HTML class to set on the div element that contains the messages.
includeEmptyContainer
boolean
No
false
Includes the div container even if the Flash is empty.
encode
boolean
No
true
Use this argument to decide whether the output of the function should be encoded in order to prevent Cross Site Scripting (XSS) attacks. Set it to true to encode all relevant output for the specific HTML element in question (e.g. tag content, attribute values, and URLs). For HTML elements that have both tag content and attribute values you can set this argument to attributes to only encode attribute values and not tag content.
// 1. Display all Flash messages in a view
// In the controller action:
flashInsert(success="Your post was successfully submitted.");
flashInsert(alert="Don't forget to tweet about this post!");
flashInsert(error="This is an error message.");
// In the layout or view:
writeOutput(flashMessages());
// Generates (keys sorted alphabetically):
// <div class="flash-messages">
// <p class="alert-message">Don't forget to tweet about this post!</p>
// <p class="error-message">This is an error message.</p>
// <p class="success-message">Your post was successfully submitted.</p>
// </div>
// 2. Show only a single Flash key using the `key` alias
writeOutput(flashMessages(key="success"));
// Generates:
// <div class="flash-messages">
// <p class="success-message">Your post was successfully submitted.</p>
// </div>
// 3. Show a specific set of keys in a defined order using `keys`
writeOutput(flashMessages(keys="success,alert"));
// Generates (in the order supplied, not alphabetically):
// <div class="flash-messages">
// <p class="success-message">Your post was successfully submitted.</p>
// <p class="alert-message">Don't forget to tweet about this post!</p>
// </div>
// 4. Always render the container div, even when the Flash is empty
writeOutput(flashMessages(includeEmptyContainer=true));
// Generates:
// <div class="flash-messages"></div>
// 5. Use a custom CSS class on the outer container
writeOutput(flashMessages(class="notifications"));
// Generates:
// <div class="notifications">
// <p class="alert-message">Don't forget to tweet about this post!</p>
// <p class="error-message">This is an error message.</p>
// <p class="success-message">Your post was successfully submitted.</p>
// </div>
// 1. Add a single float column to a new table
t.float("price");
// 2. Add a float column with a default value
t.float(columnNames="rating", default="0.0");
// 3. Add multiple float columns at once
t.float("latitude,longitude");
// 4. Add a float column that does not allow NULL values
t.float(columnNames="score", allowNull=false);
// 5. Use float() within a createTable migration
t = createTable("measurements");
t.float("temperature");
t.float(columnNames="humidity,pressure", default="0.0");
t.timestamps();
t.create();
Removes a row from wheels_migrator_versions without running
down(). Only orphan versions (those with no matching local file)
can be forgotten — for legitimate rollbacks, use migrate down.
Returns: {success, removed, message}
Name
Type
Required
Default
Description
version
string
Yes
The version string to forget (digits only after sanitisation).
Generates a 36-character UUID compatible with SQL Server's uniqueidentifier.
// 1. Generate a UUID and store it in a variable
newId = generateUUID();
// newId -> "550e8400-e29b-41d4-a716-446655440000" (36-character UUID)
// 2. Use generateUUID() to assign a unique identifier before saving a record
post = model("Post").new(title="Hello World");
post.externalId = generateUUID();
post.save();
// 3. Generate a UUID compatible with SQL Server's uniqueidentifier column
// Useful when inserting records that need a GUID primary key
token = generateUUID();
// token -> "a1b2c3d4-e5f6-7890-abcd-ef1234567890"
Returns the current setting for the supplied Wheels setting or the current default for the supplied Wheels function argument.
Name
Type
Required
Default
Description
name
string
Yes
Variable name to get setting for.
functionName
string
No
Function name to get setting for.
// 1. Get the current value of a global Wheels setting
setting = get("tableNamePrefix");
// setting -> "" (or whatever prefix has been configured)
// 2. Get the current value of a different global setting
dsName = get("dataSourceName");
// dsName -> "myAppDB"
// 3. Get the default for a specific function argument
// (useful for inspecting function-level defaults set via set())
msg = get(functionName="validatesConfirmationOf", name="message");
// msg -> "[property] should match confirmation"
Create a route that matches a URL requiring an HTTP GET method. We recommend only using this matcher to expose actions that display data. See post, patch, delete, and put for matchers that are appropriate for actions that change data in your database.
Name
Type
Required
Default
Description
name
string
No
Camel-case name of route to reference when build links and form actions (e.g., blogPost).
pattern
string
No
Overrides the URL pattern that will match the route. The default value is a dasherized version of name (e.g., a name of blogPost generates a pattern of blog-post).
to
string
No
Set controller##action combination to map the route to. You may use either this argument or a combination of controller and action.
controller
string
No
Map the route to a given controller. This must be passed along with the action argument.
action
string
No
Map the route to a given action within the controller. This must be passed along with the controller argument.
package
string
No
Indicates a subfolder that the controller will be referenced from (but not added to the URL pattern). For example, if you set this to admin, the controller will be located at admin/YourController.cfc, but the URL path will not contain admin/.
on
string
No
If this route is within a nested resource, you can set this argument to member or collection. A member route contains a reference to the resource's key, while a collection route does not.
redirect
string
No
Redirect via 302 to this URL when this route is matched. Has precedence over controller/action. Use either an absolute link like /about/, or a full canonical link.
<cfscript>
mapper()
// 1. Basic GET route using the `to` shorthand (controller##action)
// Route name: post
// Example URL: /posts/my-post-title
// Controller: Posts
// Action: show
.get(name="post", pattern="posts/[slug]", to="posts##show")
// 2. Route using separate `controller` and `action` arguments
// Route name: posts
// Example URL: /posts
// Controller: Posts
// Action: index
.get(name="posts", controller="posts", action="index")
// 3. Custom URL pattern that differs from the route name
// Route name: authors
// Example URL: /the-scribes
// Controller: Authors
// Action: index
.get(name="authors", pattern="the-scribes", to="authors##index")
// 4. Package (subfolder) scoping — keeps the package out of the URL
// Route name: commerceCart
// Example URL: /cart
// Controller: commerce.Carts
// Action: show
.get(name="cart", to="carts##show", package="commerce")
// 5. Multi-line format for readability, with package scoping
// Route name: extranetEditProfile
// Example URL: /profile/edit
// Controller: extranet.Profiles
// Action: edit
.get(
name="editProfile",
pattern="profile/edit",
to="profiles##edit",
package="extranet"
)
// 6. Permanent redirect — useful for renamed or moved URLs
// Example URL: /old-about -> 302 redirect to /about
.get(name="oldAbout", pattern="old-about", redirect="/about")
// 7. GET routes scoped inside a nested resource
.resources(name="users", nested=true)
// Route name: activatedUsers
// Example URL: /users/activated
// Controller: Users
// Action: activated
.get(name="activated", to="users##activated", on="collection")
// Route name: preferencesUser
// Example URL: /users/391/preferences
// Controller: Preferences
// Action: index
.get(name="preferences", to="preferences##index", on="member")
.end()
.end();
</cfscript>
Searches db/migrate folder for migrations. Whilst you can use this in your application, the recommended usage is via either the CLI or the provided GUI interface
$getVersionsPreviouslyMigrated). Callers that already hold the list
(doctor, info, migrateTo) pass it through to avoid re-running the
tracking-table probe chain; when empty it is computed here.
Name
Type
Required
Default
Description
path
string
No
[runtime expression]
Path to Migration Files: defaults to /app/migrator/migrations/
previousMigrationList
string
No
Optional precomputed applied-versions list (from
// 1. Get all available migrations and find the latest version
migrations = application.wheels.migrator.getAvailableMigrations();
if (arrayLen(migrations)) {
latestVersion = migrations[arrayLen(migrations)].version;
} else {
latestVersion = 0;
}
// 2. List pending (not yet run) migrations
migrations = application.wheels.migrator.getAvailableMigrations();
for (migration in migrations) {
if (migration.status != "migrated") {
writeOutput(migration.version & " - " & migration.name);
}
}
// 3. Use a custom migrations path
migrations = application.wheels.migrator.getAvailableMigrations(path=expandPath("/app/db/migrate/"));
Returns current database version. Whilst you can use this in your application, the recommended usage is via either the CLI or the provided GUI interface
// 1. Get current database version
currentVersion = application.wheels.migrator.getCurrentMigrationVersion();
// currentVersion -> "20240315120000" (timestamp-style version string, or "0" if no migrations have run)
// 2. Display version status in a maintenance view
currentVersion = application.wheels.migrator.getCurrentMigrationVersion();
if (currentVersion == "0") {
writeOutput("No migrations have been applied yet.");
} else {
writeOutput("Database is at version: " & currentVersion);
}
Primarily used for testing to get information about emails sent during the request.
// 1. Assert that exactly one email was sent during the action (in a test)
processSignup();
emails = getEmails();
assert("arrayLen(emails) eq 1");
assert("emails[1].to eq 'newuser@example.com'");
assert("emails[1].subject eq 'Welcome!'");
// 2. Inspect all emails sent during a request
emails = getEmails();
for (email in emails) {
writeOutput(email.to & " — " & email.subject);
}
// 3. Return an empty array when no emails were sent
emails = getEmails();
// emails -> []
Primarily used for testing to get information about files sent during the request.
// 1. Assert that exactly one file was sent during the action (in a test)
processDownload();
files = getFiles();
assert("arrayLen(files) eq 1");
assert("files[1].name eq 'report.pdf'");
// 2. Inspect all files sent during a request
files = getFiles();
for (file in files) {
writeOutput(file.name & " — " & file.type);
}
// 3. Return an empty array when no files were sent
files = getFiles();
// files -> []
Primarily used for testing to establish whether the current request has performed a redirect.
// 1. Assert that a redirect was performed to a specific URL (in a test)
processDelete();
redirect = getRedirect();
assert("structCount(redirect) gt 0");
assert("redirect.url eq '/users'");
// 2. Inspect the full redirect struct after an action runs
submitLogin();
redirect = getRedirect();
// redirect.url -> "/dashboard"
// redirect.statusCode -> 302
// redirect.addToken -> false
// 3. Confirm no redirect was performed (action rendered a view instead)
showProfile();
redirect = getRedirect();
// redirect -> {}
// 1. Use the table name prefix when running a custom query inside a model method
function getDisabledUsers() {
local.q = queryExecute(
"SELECT * FROM #this.getTableNamePrefix()#users WHERE disabled = 1",
[],
{datasource: get("dataSourceName")}
);
return local.q;
}
// 2. Log the configured prefix to verify model setup
prefix = model("User").getTableNamePrefix();
// prefix -> "app_" (or "" if none is set)
Group routes together with shared attributes like path prefix, name prefix, and constraints without implying a controller package or namespace. Unlike namespace() (which maps to a subfolder and URL prefix) or package() (which maps to a subfolder), group() is a pure organizational grouping mechanism.
Name
Type
Required
Default
Description
name
string
No
Name to prepend to child route names for use when building links, forms, and other URLs.
path
string
No
URL path prefix to apply to all child routes.
constraints
struct
No
Variable patterns (regex constraints) to apply to all child routes.
callback
any
No
A callback function to define nested routes within this group. If provided, the group is automatically closed when the callback completes.
<cfscript>
// 1. Group routes under a shared path prefix (open/close style)
mapper()
.group(path="admin")
// Route URL: /admin/dashboard
.get(name="dashboard", to="dashboard##index")
// Route URL: /admin/reports
.get(name="reports", to="reports##index")
.end()
.end();
// 2. Group with both a path prefix and a name prefix
mapper()
.group(path="account", name="account")
// Route name: accountSettings
// Example URL: /account/settings
.get(name="settings", to="settings##show")
// Route name: accountBilling
// Example URL: /account/billing
.get(name="billing", to="billing##show")
.end()
.end();
// 3. Group with regex constraints applied to all child routes
mapper()
.group(path="products", constraints={id: "\d+"})
// Only matches numeric :id segments
.get(name="productShow", pattern="[id]", to="products##show")
.put(name="productUpdate", pattern="[id]", to="products##update")
.end()
.end();
// 4. Group using a callback function (auto-closes the group)
mapper()
.group(
path = "reports",
name = "report",
callback = function(mapper) {
// Route name: reportSales
// Example URL: /reports/sales
mapper.get(name="sales", to="reports##sales");
// Route name: reportExpenses
// Example URL: /reports/expenses
mapper.get(name="expenses", to="reports##expenses");
}
)
.end();
</cfscript>
Encodes a value for safe HTML output. Use in templates to prevent XSS:
#h(user.name)# instead of #user.name#.
Name
Type
Required
Default
Description
value
any
Yes
The value to encode for HTML output. Converted to string if not already.
// 1. Safely output user-supplied text in a view template
writeOutput(h(user.name));
// If user.name is "<script>alert('xss')</script>", outputs the
// HTML-encoded form: <script>alert(&##x27;xss&##x27;)</script>
// 2. Encode a variable inline in a cfoutput block
// Instead of: <cfoutput>##user.bio##</cfoutput>
// Use: <cfoutput>##h(user.bio)##</cfoutput>
encodedBio = h(user.bio);
// 3. Encode a non-string value (converted to string automatically)
rating = 4.5;
writeOutput(h(rating));
// rating -> "4.5"
Returns true if the specified property (or any if none was passed in) has been changed but not yet saved to the database.
Will also return true if the object is new and no record for it exists in the database.
Name
Type
Required
Default
Description
property
string
No
Name of property to check for change.
// 1. Check if a specific property has changed before saving
member = model("member").findByKey(params.memberId);
member.email = params.newEmail;
if (member.hasChanged("email")) {
// Send a confirmation email before committing the change
sendEmailChangeNotification(member);
}
// 2. Check if any property has changed (no argument)
user = model("User").findByKey(params.userId);
user.setProperties(params.user);
if (user.hasChanged()) {
// At least one property differs from the persisted state
user.save();
}
// 3. Use the dynamic shorthand — automatically generated per property
order = model("Order").findByKey(params.orderId);
order.status = "shipped";
if (order.statusHasChanged()) {
// Equivalent to: order.hasChanged("status")
notifyCustomer(order);
}
// 4. New (unsaved) objects always return true — no persisted record exists yet
newPost = model("Post").new(title="Hello");
writeOutput(newPost.hasChanged()); // -> true
Returns true if the object has any errors.
You can also limit to only check a specific property or name for errors.
Name
Type
Required
Default
Description
property
string
No
Name of the property to check if there are any errors set on.
name
string
No
Error name to check if there are any errors set with.
// 1. Check if a post object has any errors at all
if (post.hasErrors()) {
// Redirect user back to the form to correct errors
}
// 2. Check if a specific property has errors
if (post.hasErrors(property="title")) {
// The title field has at least one error
}
// 3. Check if any errors were set with a specific name
if (post.hasErrors(name="uniquenessViolation")) {
// Handle uniqueness error specifically
}
Sets up a hasMany association between this model and the specified one.
Name
Type
Required
Default
Description
name
string
Yes
Gives the association a name that you refer to when working with the association (in the include argument to findAll, to name one example).
modelName
string
No
Name of associated model (usually not needed if you follow Wheels conventions because the model name will be deduced from the name argument).
foreignKey
string
No
Foreign key property name (usually not needed if you follow Wheels conventions since the foreign key name will be deduced from the name argument).
joinKey
string
No
Column name to join to if not the primary key (usually not needed if you follow Wheels conventions since the join key will be the table's primary key/keys).
joinType
string
No
outer
Use to set the join type when joining associated tables. Possible values are inner (for INNER JOIN) and outer (for LEFT OUTER JOIN).
dependent
string
No
false
Defines how to handle dependent model objects when you delete an object from this model. delete / deleteAll deletes the record(s) (deleteAll bypasses object instantiation). remove / removeAll sets the forein key field(s) to NULL (removeAll bypasses object instantiation).
shortcut
string
No
Set this argument to create an additional dynamic method that gets the object(s) from the other side of a many-to-many association.
through
string
No
[runtime expression]
Set this argument if you need to override Wheels conventions when using the shortcut argument. Accepts a list of two association names representing the chain from the opposite side of the many-to-many relationship to this model.
as
string
No
Set this argument to declare a polymorphic hasMany association. The child model stores the parent type in a {as}Type column alongside the foreign key {as}Id.
// 1. Basic usage – a Post has many comments (foreign key `postId` lives on the `comments` table)
// In models/Post.cfc config()
hasMany("comments");
// 2. Set up a many-to-many shortcut so readers can access publications directly
// In models/Reader.cfc config()
hasMany(name="subscriptions", shortcut="publications");
// 3. Automatically delete all associated comments (bypassing object instantiation) when the parent is deleted
// In models/Post.cfc config()
hasMany(name="comments", dependent="deleteAll");
// 4. Instantiate and call each comment's beforeDelete callback when deleting dependents
// In models/Post.cfc config()
hasMany(name="comments", dependent="delete");
// 5. Override the many-to-many shortcut chain when association names differ from model names
// In models/Customer.cfc config()
hasMany(name="subscriptions", shortcut="magazines", through="publication,subscriptions");
// In models/Subscription.cfc config()
belongsTo("customer");
belongsTo("publication");
// In models/Publication.cfc config()
hasMany("subscriptions");
// 6. Specify a custom foreign key when not following Wheels naming conventions
// In models/Author.cfc config()
hasMany(name="articles", foreignKey="writtenByAuthorId");
// 7. Use an inner join instead of the default outer join when including the association
// In models/Category.cfc config()
hasMany(name="products", joinType="inner");
// 8. Polymorphic hasMany – a model acts as a parent for a shared `comments` child model
// In models/Photo.cfc config() (the `as` value matches the `polymorphic` interface name on the child)
hasMany(name="comments", as="commentable");
// In models/Comment.cfc config()
belongsTo(name="commentable", polymorphic=true);
Used as a shortcut to output the proper form elements for an association.
Note: Pass any additional arguments like class, rel, and id, and the generated tag will also include those values as HTML attributes.
Name
Type
Required
Default
Description
objectName
string
Yes
Name of the variable containing the parent object to represent with this form field.
association
string
Yes
Name of the association set in the parent object to represent with this form field.
keys
string
Yes
Primary keys associated with this form field. Note that these keys should be listed in the order that they appear in the database table.
label
string
No
The label text to use in the form control.
labelPlacement
string
No
Whether to place the label before, after, or wrapped around the form control. Label text placement can be controlled using aroundLeft or aroundRight.
prepend
string
No
String to prepend to the form control. Useful to wrap the form control with HTML tags.
append
string
No
String to append to the form control. Useful to wrap the form control with HTML tags.
prependToLabel
string
No
String to prepend to the form control's label. Useful to wrap the form control with HTML tags.
appendToLabel
string
No
String to append to the form control's label. Useful to wrap the form control with HTML tags.
errorElement
string
No
HTML tag to wrap the form control with when the object contains errors.
errorClass
string
No
The class name of the HTML tag that wraps the form control when there are errors.
encode
any
No
true
Use this argument to decide whether the output of the function should be encoded in order to prevent Cross Site Scripting (XSS) attacks. Set it to true to encode all relevant output for the specific HTML element in question (e.g. tag content, attribute values, and URLs). For HTML elements that have both tag content and attribute values you can set this argument to attributes to only encode attribute values and not tag content.
id
string
No
Optional. Explicit ID for the generated checkbox input. If not provided, an ID will be generated automatically.
// 1. Show check boxes for associating authors with the current book
<cfloop query="authors">
#hasManyCheckBox(
label=authors.fullName,
objectName="book",
association="bookAuthors",
keys="#book.key()#,#authors.id#"
)#
</cfloop>
// 2. Wrap each checkbox and label in a div using prepend/append
<cfloop query="authors">
#hasManyCheckBox(
label=authors.fullName,
labelPlacement="after",
objectName="book",
association="bookAuthors",
keys="#book.key()#,#authors.id#",
prepend="<div class=""author-option"">",
append="</div>"
)#
</cfloop>
// 3. Supply an explicit ID and custom error styling
<cfloop query="tags">
#hasManyCheckBox(
label=tags.name,
objectName="post",
association="postTags",
keys="#post.key()#,#tags.id#",
id="tag-#tags.id#",
errorElement="span",
errorClass="field-error"
)#
</cfloop>
Used as a shortcut to output the proper form elements for an association.
Note: Pass any additional arguments like class, rel, and id, and the generated tag will also include those values as HTML attributes.
Name
Type
Required
Default
Description
objectName
string
Yes
Name of the variable containing the parent object to represent with this form field.
association
string
Yes
Name of the association set in the parent object to represent with this form field.
property
string
Yes
Name of the property in the child object to represent with this form field.
keys
string
Yes
Primary keys associated with this form field. Note that these keys should be listed in the order that they appear in the database table.
tagValue
string
Yes
The value of the radio button when selected.
checkIfBlank
boolean
No
false
Whether or not to check this form field as a default if there is a blank value set for the property.
label
string
No
The label text to use in the form control.
encode
any
No
true
Use this argument to decide whether the output of the function should be encoded in order to prevent Cross Site Scripting (XSS) attacks. Set it to true to encode all relevant output for the specific HTML element in question (e.g. tag content, attribute values, and URLs). For HTML elements that have both tag content and attribute values you can set this argument to attributes to only encode attribute values and not tag content.
// 1. Show radio buttons for selecting a default address for the current author (loops over a query of addresses)
<cfoutput>
<cfloop query="addresses">
#hasManyRadioButton(
objectName="author",
association="addresses",
property="isDefault",
keys="#author.key()#,#addresses.id#",
tagValue="1",
label=addresses.title
)#
</cfloop>
</cfoutput>
// 2. Same loop, but mark the radio button as checked when the property is blank (no default set yet)
<cfoutput>
<cfloop query="addresses">
#hasManyRadioButton(
objectName="author",
association="addresses",
property="isDefault",
keys="#author.key()#,#addresses.id#",
tagValue="1",
checkIfBlank=true,
label=addresses.title
)#
</cfloop>
</cfoutput>
Sets up a hasOne association between this model and the specified one.
Name
Type
Required
Default
Description
name
string
Yes
Gives the association a name that you refer to when working with the association (in the include argument to findAll, to name one example).
modelName
string
No
Name of associated model (usually not needed if you follow Wheels conventions because the model name will be deduced from the name argument).
foreignKey
string
No
Foreign key property name (usually not needed if you follow Wheels conventions since the foreign key name will be deduced from the name argument).
joinKey
string
No
Column name to join to if not the primary key (usually not needed if you follow Wheels conventions since the join key will be the table's primary key/keys).
joinType
string
No
outer
Use to set the join type when joining associated tables. Possible values are inner (for INNER JOIN) and outer (for LEFT OUTER JOIN).
dependent
string
No
false
Defines how to handle dependent model objects when you delete an object from this model. delete / deleteAll deletes the record(s) (deleteAll bypasses object instantiation). remove / removeAll sets the forein key field(s) to NULL (removeAll bypasses object instantiation).
as
string
No
Set this argument to declare a polymorphic hasOne association. The child model stores the parent type in a {as}Type column alongside the foreign key {as}Id.
// 1. Specify that instances of this model have one profile. (The associated model's table, not the current one, should have the foreign key column.)
hasOne("profile");
// 2. Same as above but setting `joinType` to `inner`, meaning this model should always have a matching record in the `profiles` table.
hasOne(name="profile", joinType="inner");
// 3. Automatically delete the associated `profile` record whenever this object is deleted.
hasOne(name="profile", dependent="delete");
// 4. Declare a polymorphic `hasOne` association so that multiple models can each have one image via a shared interface.
hasOne(name="image", as="imageable");
Returns true if the specified property name exists on the model.
Name
Type
Required
Default
Description
property
string
Yes
Name of property to inspect.
// 1. Check whether a property exists on a new object after setting it
employee = model("Employee").new();
employee.firstName = "Jane";
employee.hasProperty("firstName"); // -> true
employee.hasProperty("salary"); // -> false (not set on this object)
// 2. Use the equivalent dynamic method (has<PropertyName>)
employee.hasFirstName(); // -> true
employee.hasSalary(); // -> false
// 3. Guard logic before accessing a property
user = model("User").findOne(where="email='jane@example.com'");
if (user.hasProperty("avatarUrl")) {
writeOutput(user.avatarUrl);
}
Register a health check route at /health (or a custom path). Returns a JSON response with status and timestamp by default, or delegates to a custom controller action.
This is useful for container orchestration (Kubernetes liveness/readiness probes), load balancer health checks, and monitoring tools.
Name
Type
Required
Default
Description
to
string
No
wheels#health
Set controller##action combination for a custom health check handler. If not provided, a default handler returns {"status":"ok","timestamp":"..."}.
path
string
No
health
Override the URL path. Defaults to "health".
name
string
No
health
Override the route name. Defaults to "health".
<cfscript>
mapper()
// 1. Register the default health check route at /health.
// Responds with {"status":"ok","timestamp":"..."} — no controller needed.
.health()
.root(to="home##index")
.wildcard()
.end();
// 2. Override the URL path and route name.
// Accessible at /healthz instead of /health.
mapper()
.health(path="healthz", name="healthz")
.root(to="home##index")
.end();
// 3. Delegate to a custom controller action for advanced health checks
// (e.g. database ping, cache connectivity).
mapper()
.health(to="system##health")
.root(to="home##index")
.end();
</cfscript>
Builds and returns a string containing a hidden field form control based on the supplied objectName and property.
Note: Pass any additional arguments like class, rel, and id, and the generated tag will also include those values as HTML attributes.
Name
Type
Required
Default
Description
objectName
any
Yes
The variable name of the object to build the form control for.
property
string
Yes
The name of the property to use in the form control.
association
string
No
The name of the association that the property is located on. Used for building nested forms that work with nested properties. If you are building a form with deep nesting, simply pass in a list to the nested object, and Wheels will figure it out.
position
string
No
The position used when referencing a hasMany relationship in the association argument. Used for building nested forms that work with nested properties. If you are building a form with deep nestings, simply pass in a list of positions, and Wheels will figure it out.
encode
boolean
No
true
Use this argument to decide whether the output of the function should be encoded in order to prevent Cross Site Scripting (XSS) attacks. Set it to true to encode all relevant output for the specific HTML element in question (e.g. tag content, attribute values, and URLs). For HTML elements that have both tag content and attribute values you can set this argument to attributes to only encode attribute values and not tag content.
// 1. Basic usage: embed the user's id as a hidden field
#hiddenField(objectName="user", property="id")#
// -> <input type="hidden" id="user-id" name="user[id]" value="42">
// 2. Carry a token value using a class attribute for JavaScript hooks
#hiddenField(objectName="user", property="csrfToken", class="js-token")#
// -> <input type="hidden" id="user-csrf-token" name="user[csrfToken]" class="js-token" value="abc123">
// 3. Nested form — pass the parent's id through a hasMany association
#hiddenField(objectName="order", property="id", association="lineItems", position="1")#
// -> <input type="hidden" id="order-line-items-1-id" name="order[lineItems][1][id]" value="7">
Builds and returns a string containing a hidden field form control based on the supplied name.
Note: Pass any additional arguments like class, rel, and id, and the generated tag will also include those values as HTML attributes.
Name
Type
Required
Default
Description
name
string
Yes
Name to populate in tag's name attribute.
value
string
No
Value to populate in tag's value attribute.
encode
boolean
No
true
Use this argument to decide whether the output of the function should be encoded in order to prevent Cross Site Scripting (XSS) attacks. Set it to true to encode all relevant output for the specific HTML element in question (e.g. tag content, attribute values, and URLs). For HTML elements that have both tag content and attribute values you can set this argument to attributes to only encode attribute values and not tag content.
// 1. Basic usage with a name and value
#hiddenFieldTag(name="userId", value=user.id)#
// -> <input type="hidden" name="userId" value="42">
// 2. Embed a CSRF token in a standalone form
#hiddenFieldTag(name="csrfToken", value=session.csrfToken)#
// -> <input type="hidden" name="csrfToken" value="a1b2c3d4...">
// 3. Pass extra HTML attributes via additional arguments
#hiddenFieldTag(name="returnTo", value="/dashboard", id="returnToField")#
// -> <input type="hidden" name="returnTo" value="/dashboard" id="returnToField">
Highlights the phrase(s) everywhere in the text if found by wrapping them in span tags.
Name
Type
Required
Default
Description
text
string
Yes
Text to search in.
phrase
string
No
Phrase (or list of phrases) to highlight. This argument is also aliased as phrases.
delimiter
string
No
,
Delimiter to use when passing in multiple phrases.
tag
string
No
span
HTML tag to use to wrap the highlighted phrase(s).
class
string
No
highlight
Class to use in the tags wrapping highlighted phrase(s).
encode
boolean
No
true
Use this argument to decide whether the output of the function should be encoded in order to prevent Cross Site Scripting (XSS) attacks. Set it to true to encode all relevant output for the specific HTML element in question (e.g. tag content, attribute values, and URLs). For HTML elements that have both tag content and attribute values you can set this argument to attributes to only encode attribute values and not tag content.
// 1. Highlight a single phrase in text
// Outputs: You searched for: <span class="highlight">CFWheels</span> framework
result = highlight(text="You searched for: CFWheels framework", phrase="CFWheels");
// 2. Highlight multiple phrases using a comma-delimited list
// Outputs: <span class="highlight">Open</span> source <span class="highlight">CFML</span> framework
result = highlight(text="Open source CFML framework", phrase="Open,CFML");
// 3. Highlight with a custom tag and CSS class
// Outputs: Learn <strong class="match">ColdFusion</strong> today
result = highlight(text="Learn ColdFusion today", phrase="ColdFusion", tag="strong", class="match");
Builds and returns a string containing one select form control for the hours of the day based on the supplied name.
Name
Type
Required
Default
Description
name
string
Yes
Name to populate in tag's name attribute.
selected
string
No
The hour that should be selected initially.
includeBlank
any
No
false
Whether to include a blank option in the select form control. Pass true to include a blank line or a string that should represent what display text should appear for the empty value (for example, "- Select One -").
label
string
No
The label text to use in the form control.
labelPlacement
string
No
around
Whether to place the label before, after, or wrapped around the form control. Label text placement can be controlled using aroundLeft or aroundRight.
prepend
string
No
String to prepend to the form control. Useful to wrap the form control with HTML tags.
append
string
No
String to append to the form control. Useful to wrap the form control with HTML tags.
prependToLabel
string
No
String to prepend to the form control's label. Useful to wrap the form control with HTML tags.
appendToLabel
string
No
String to append to the form control's label. Useful to wrap the form control with HTML tags.
twelveHour
boolean
No
false
whether to display the hours in 24 or 12 hour format. 12 hour format has AM/PM drop downs
encode
any
No
true
Use this argument to decide whether the output of the function should be encoded in order to prevent Cross Site Scripting (XSS) attacks. Set it to true to encode all relevant output for the specific HTML element in question (e.g. tag content, attribute values, and URLs). For HTML elements that have both tag content and attribute values you can set this argument to attributes to only encode attribute values and not tag content.
// 1. Basic usage — render a select control for hours of the day
#hourSelectTag(name="hourOfMeeting", selected=params.hourOfMeeting)#
// 2. Show hours in 12-hour (AM/PM) format instead of 24-hour format
#hourSelectTag(name="hourOfMeeting", selected=params.hourOfMeeting, twelveHour=true)#
// 3. Include a blank option as a placeholder prompt and add a label
#hourSelectTag(name="startHour", selected=params.startHour, includeBlank="-- Select Hour --", label="Start Hour")#
Returns readable text by capitalizing and converting camel casing to multiple words.
Name
Type
Required
Default
Description
text
string
Yes
Text to humanize.
except
string
No
A list of strings (space separated) to replace within the output.
// 1. Humanize a camelCase string
result = humanize("wheelsIsAFramework");
// result -> "Wheels Is A Framework"
// 2. Humanize a string and replace an abbreviation using the except argument
result = humanize("wheelsIsACfmlFramework", "CFML");
// result -> "Wheels Is A CFML Framework"
// 3. Humanize a multi-word property name from a model attribute
result = humanize("firstName");
// result -> "First Name"
Converts camelCase strings to lowercase strings with hyphens as word delimiters instead. Example: myVariable becomes my-variable.
Name
Type
Required
Default
Description
string
string
Yes
The string to hyphenize.
// 1. Basic camelCase to hyphenated string
result = hyphenize("myBlogPost");
// result -> "my-blog-post"
// 2. Single word (no change)
result = hyphenize("hello");
// result -> "hello"
// 3. Used in URL slug generation
slug = hyphenize("userProfileSettings");
// slug -> "user-profile-settings"
Use this method to specify which columns cannot be used by the wheels ORM.
Name
Type
Required
Default
Description
columns
array
No
[runtime expression]
Array of columns names that will be ignored.
// 1. Ignore a single column in the model's config() method
// In app/models/User.cfc
component extends="Model" {
function config() {
ignoredColumns(columns=["legacyField"]);
}
}
// 2. Ignore multiple columns so they are excluded from Wheels ORM property mapping
// In app/models/Product.cfc
component extends="Model" {
function config() {
ignoredColumns(columns=["internalCode", "deprecatedFlag", "tempCache"]);
}
}
Returns an img tag.
If the image is stored in the local images folder, the tag will also set the width, height, and alt attributes for you.
You can pass any additional arguments (e.g. class, rel, id), and the generated tag will also include those values as HTML attributes.
Name
Type
Required
Default
Description
source
string
Yes
The file name of the image if it's available in the local file system (i.e. ColdFusion will be able to access it). Provide the full URL if the image is on a remote server.
onlyPath
boolean
No
true
host
string
No
protocol
string
No
port
numeric
No
0
encode
boolean
No
true
Use this argument to decide whether the output of the function should be encoded in order to prevent Cross Site Scripting (XSS) attacks. Set it to true to encode all relevant output for the specific HTML element in question (e.g. tag content, attribute values, and URLs). For HTML elements that have both tag content and attribute values you can set this argument to attributes to only encode attribute values and not tag content.
required
boolean
No
true
// 1. Output an img tag for a local image (width, height, and alt are auto-detected)
writeOutput(imageTag("logo.png"));
// 2. Output an img tag for an image hosted on an external server
writeOutput(imageTag(source="https://example.com/images/logo.png", alt="Company Logo"));
// 3. Output an img tag with additional HTML attributes
writeOutput(imageTag(source="logo.png", class="logo", id="mainLogo"));
// 4. Output an img tag without requiring the file to exist locally (useful in development)
writeOutput(imageTag(source="placeholder.png", required=false));
Used to output the content for a particular section in a layout.
Name
Type
Required
Default
Description
name
string
No
body
Name of layout section to return content for.
defaultValue
string
No
What to display as a default if the section is not defined.
// 1. Output the main page body inside a layout (default section is "body")
// In `app/views/layout.cfm`:
<html>
<head>
<title>My Site</title>
</head>
<body>
<cfoutput>
#includeContent()#
</cfoutput>
</body>
</html>
// 2. Define a named section in a view, then render it in the layout
// In `app/views/blog/show.cfm`:
contentFor(head='<meta name="description" content="Read our latest post">');
// In `app/views/layout.cfm`:
<html>
<head>
<title>My Site</title>
<cfoutput>#includeContent("head")#</cfoutput>
</head>
<body>
<cfoutput>#includeContent()#</cfoutput>
</body>
</html>
// 3. Provide a default value when a section may not have been defined
<cfoutput>
#includeContent(name="sidebar", defaultValue="<p>No sidebar content.</p>")#
</cfoutput>
Used as a shortcut to check if the specified IDs are a part of the main form object.
This method should only be used for hasMany associations.
Name
Type
Required
Default
Description
objectName
string
Yes
Name of the variable containing the parent object to represent with this form field.
association
string
Yes
Name of the association set in the parent object to represent with this form field.
keys
string
Yes
Primary keys associated with this form field. Note that these keys should be listed in the order that they appear in the database table.
// 1. Check whether a customer is already subscribed to a particular publication via a hasMany join
// (Note: keys should be listed in the order they appear in the join table columns)
if (includedInObject(objectName="customer", association="subscriptions", keys="#customer.key()#,#swimsuitEdition.id#")) {
writeOutput("Already subscribed.");
} else {
writeOutput("Not yet subscribed.");
}
// 2. Use the return value to find the position of the associated object in the array
position = includedInObject(objectName="order", association="lineItems", keys="#lineItem.key()#");
// Returns false when not found, or the 1-based index position when found
// position -> 3 (the associated lineItem is at index 3 in order.lineItems)
// 3. Guard against adding duplicate associations before creating a new join record
if (!includedInObject(objectName="student", association="courses", keys="#course.key()#")) {
student.courses = ArrayAppend(student.courses, course);
}
Includes the contents of another layout file.
This is usually used to include a parent layout from within a child layout.
Name
Type
Required
Default
Description
name
string
No
layout
Name of the layout file to include.
// 1. Include the default parent layout from within a child layout
// (looks for `app/views/layout.cfm` by default)
#includeLayout()#
// 2. Include a specific parent layout by path
// (looks for `app/views/layouts/application.cfm`)
#includeLayout("/layouts/application.cfm")#
// 3. Pass section content to the parent layout before including it
// Capture sidebar markup to make it available in the parent layout
<cfsavecontent variable="sidebar">
<nav>
#includePartial("categories")#
</nav>
</cfsavecontent>
<cfset contentFor(sidebar=sidebar)>
// Then pull in the parent layout that renders the sidebar via includeContent()
#includeLayout("/layouts/application.cfm")#
Includes the specified partial file in the view.
Similar to using cfinclude but with the ability to cache the result and use Wheels-specific file look-up.
By default, Wheels will look for the file in the current controller's view folder.
To include a file relative from the base views folder, you can start the path supplied to partial with a forward slash.
Name
Type
Required
Default
Description
partial
any
Yes
The name of the partial file to be used. Prefix with a leading slash (/) if you need to build a path from the root views folder. Do not include the partial filename's underscore and file extension. If you want to have Wheels display the partial for a single model object, array of model objects, or a query, pass a variable containing that data into this argument.
group
string
No
If passing a query result set for the partial argument, use this to specify the field to group the query by. A new query will be passed into the partial template for you to iterate over.
cache
any
No
Number of minutes to cache the content for.
layout
string
No
The layout to wrap the content in. Prefix with a leading slash (/) if you need to build a path from the root views folder. Pass false to not load a layout at all.
spacer
string
No
HTML or string to place between partials when called using a query.
dataFunction
any
No
true
Name of controller function to load data from.
// 1. Include a partial from the current controller's view folder.
// When in the "sessions" controller, Wheels looks for "app/views/sessions/_login.cfm".
#includePartial("login")#
// 2. Include a partial relative to the root views folder using a leading slash.
// Wheels looks for "app/views/shared/_button.cfm".
#includePartial(partial="/shared/button")#
// 3. Pass a query to loop through records automatically.
// Wheels loops through the result set and renders "app/views/posts/_post.cfm" for each row.
posts = model("Post").findAll();
#includePartial(posts)#
// 4. Override the template when rendering a query.
// Provide the template path via partial and pass the query separately.
posts = model("Post").findAll();
#includePartial(partial="/shared/post", query=posts)#
// 5. Pass a single model object — Wheels renders the matching partial for its model type.
post = model("Post").findByKey(params.key);
#includePartial(post)#
// 6. Override the template when rendering a single model object.
post = model("Post").findByKey(params.key);
#includePartial(partial="/shared/post", object=post)#
// 7. Pass an array of model objects — Wheels iterates and renders the partial for each.
posts = model("Post").findAll(returnAs="objects");
#includePartial(posts)#
// 8. Override the template when rendering an array of model objects.
posts = model("Post").findAll(returnAs="objects");
#includePartial(partial="/shared/post", objects=posts)#
// 9. Cache the partial output for 30 minutes to reduce processing overhead.
#includePartial(partial="sidebar", cache=30)#
// 10. Group a query result set by a column before rendering.
// Wheels splits the query into sub-queries grouped by "categoryId"
// and passes each sub-query into "app/views/products/_product.cfm".
products = model("Product").findAll(order="categoryId");
#includePartial(partial="product", query=products, group="categoryId")#
// 11. Insert a separator string between each rendered partial in a loop.
posts = model("Post").findAll();
#includePartial(partial="post", query=posts, spacer="<hr>")#
initSSEStream()
any
controller
Initialize a streaming SSE connection that bypasses the normal Wheels rendering pipeline.
Returns a writer object that can be used with sendSSEEvent() and closeSSEStream().
This enables sending multiple events over a single connection.
Note: This bypasses layouts and after-filters. Use for true streaming endpoints only.
inject()
void
controller
Declare one or more services for injection into this controller.
Call in config(). Services are resolved when the controller instance is created.
Name
Type
Required
Default
Description
name
string
Yes
Comma-delimited list of registered service names to inject.
injectedServices()
array
controller
Return the list of declared service names for this controller.
Return a reference to the DI container for direct configuration.
// 1. Register a singleton service in `config/services.cfm` (one instance per app lifetime)
di = injector();
di.map("emailService").to("app.lib.EmailService").asSingleton();
// 2. Bind an interface name to a concrete implementation
di = injector();
di.bind("INotifier").to("app.lib.SlackNotifier").asSingleton();
// 3. Register a request-scoped service (one instance per HTTP request)
di = injector();
di.map("currentUser").to("app.lib.CurrentUserResolver").asRequestScoped();
// 4. Inspect or resolve at runtime
di = injector();
if (di.containsInstance("emailService")) {
mailer = di.getInstance("emailService");
mailer.send(to="user@example.com", subject="Welcome");
}
Inserts multiple records into the database in a single batch operation.
Accepts an array of structs where each struct represents a record to insert.
All structs must have the same set of keys (property names).
Batches in groups of 1000 to avoid database parameter limits.
Name
Type
Required
Default
Description
records
array
Yes
Array of structs, each containing property name/value pairs to insert.
timestamps
boolean
No
true
Set to false to skip automatic createdAt/updatedAt timestamping.
transaction
string
No
[runtime expression]
Set this to commit to update the database, rollback to run all the database queries but not commit them, or none to skip transaction handling altogether.
parameterize
any
No
true
Set to true to use cfqueryparam on all columns, or pass in a list of property names to use cfqueryparam on those only.
// 1. Insert multiple user records in a single batch
newUsers = [
{firstName: "Alice", lastName: "Smith", email: "alice@example.com"},
{firstName: "Bob", lastName: "Jones", email: "bob@example.com"},
{firstName: "Carol", lastName: "White", email: "carol@example.com"}
];
result = model("User").insertAll(records=newUsers);
// result -> {insertedCount: 3}
// 2. Insert records without automatic createdAt/updatedAt timestamps
rows = [
{username: "imported_1", score: 9800},
{username: "imported_2", score: 7450}
];
result = model("HighScore").insertAll(records=rows, timestamps=false);
// result -> {insertedCount: 2}
// 3. Insert a large dataset wrapped in a single transaction, using selective cfqueryparam
products = [];
for (i = 1; i <= 2500; i++) {
arrayAppend(products, {name: "Product #i#", price: RandRange(1, 999), stock: RandRange(0, 500)});
}
// Batches automatically in groups of 1000; all batches share one transaction.
result = model("Product").insertAll(
records = products,
transaction = "commit",
parameterize = "price,stock"
);
// result -> {insertedCount: 2500}
// 1. Add a single integer column to a new table
t = createTable(name='products');
t.string(columnNames='name', limit=255, allowNull=false);
t.integer(columnNames='quantity');
t.timestamps();
t.create();
// 2. Add multiple integer columns at once with a default value
t = createTable(name='scores');
t.integer(columnNames='wins,losses,draws', default=0, allowNull=false);
t.string(columnNames='playerName', limit=100, allowNull=false);
t.timestamps();
t.create();
// 3. Add an integer column with a limit when altering an existing table
t = changeTable(name='orders');
t.integer(columnNames='itemCount', default=0, allowNull=false, limit=4);
t.change();
Runs the specified method within a single database transaction.
Name
Type
Required
Default
Description
method
string
Yes
Model method to run.
transaction
string
No
commit
Set this to commit to update the database, rollback to run all the database queries but not commit them, or none to skip transaction handling altogether.
isolation
string
No
read_committed
Isolation level to be passed through to the cftransaction tag. See your CFML engine's documentation for more details about cftransaction's isolation attribute.
// 1. Run a custom model method inside a database transaction.
// Define the method on the model (e.g. Person.cfc):
public boolean function transferFunds(required any personFrom, required any personTo, required numeric amount) {
if (arguments.personFrom.withdraw(arguments.amount) && arguments.personTo.deposit(arguments.amount)) {
return true;
} else {
return false;
}
}
// Then invoke it wrapped in a transaction from a controller action:
local.david = model("Person").findOneByName("David");
local.mary = model("Person").findOneByName("Mary");
local.success = model("Person").invokeWithTransaction(method="transferFunds", personFrom=local.david, personTo=local.mary, amount=100);
// 2. Run in rollback mode to test queries without committing changes.
local.success = model("Person").invokeWithTransaction(method="transferFunds", personFrom=local.david, personTo=local.mary, amount=100, transaction="rollback");
// 3. Run with a stricter isolation level (e.g. serializable) to prevent phantom reads.
local.success = model("Person").invokeWithTransaction(method="transferFunds", personFrom=local.david, personTo=local.mary, amount=100, isolation="serializable");
Use this method to check whether you are currently in a class-level object.
// 1. Use isClass() to branch between class-level and instance-level behavior
// In a model method, detect whether the method is being called on the class
// (e.g. model("User").isAdmin(42)) or on an instance (e.g. user.isAdmin()).
function isAdmin(numeric id) {
if (isClass()) {
// Called on the class — look up the record by the provided id
return this.findByKey(arguments.id).admin;
} else {
// Called on an instance — the property is already available
return this.admin;
}
}
// 2. Guard a class-only operation
function resetAllPasswords() {
if (!isClass()) {
Throw(type="App.Error", message="resetAllPasswords must be called on the class, not an instance.");
}
this.updateAll(password="changeme");
}
Returns whether the request was a DELETE request or not.
// 1. Only process delete logic when the request method is DELETE
if (isDelete()) {
// perform delete operation
}
// 2. Assign the result to a variable for later use
requestIsDelete = isDelete();
Returns whether the request was a normal GET request or not.
// 1. Only allow GET requests in an action
if (!isGet()) {
redirectTo(action = "index");
}
// 2. Respond differently depending on the HTTP method
if (isGet()) {
// Render the form for display
user = model("User").findByKey(params.key);
} else {
// Handle a non-GET submission
renderNothing();
}
// 3. Store the result to use in a conditional
requestIsGet = isGet();
// requestIsGet -> true (for a normal page request), false otherwise
Returns whether the request was a HEAD request or not.
// 1. Respond to a HEAD request by rendering nothing
if (isHead()) {
renderNothing();
}
// 2. Restrict an action to HEAD requests only
if (!isHead()) {
renderText("Method not allowed");
}
// 3. Store the result to use in a conditional
requestIsHead = isHead();
// requestIsHead -> true when the HTTP method is HEAD, false otherwise
Use this method to check whether you are currently in an instance object.
// 1. Branch logic inside a shared model method based on class vs. instance context
function memberIsAdmin() {
if (isInstance()) {
// Called on an instance object — property is already loaded
return this.admin;
} else {
// Called on the class — look up the record first
return this.findByKey(arguments.id).admin;
}
}
// 2. Use isInstance() in config() to guard instance-only setup
component extends="Model" {
function config() {
if (isInstance()) {
// Instance-specific initialization (rarely needed; shown for contrast)
} else {
// Class-level configuration: validations, associations, callbacks
validatesPresenceOf(properties="username,email");
hasMany(name="posts");
}
}
}
// 3. Pair with isClass() to make the intent explicit
function label() {
if (isClass()) {
return "User (class)";
}
return "User #this.id#";
}
Returns true if this object hasn't been saved yet (in other words, no matching record exists in the database yet).
Returns false if a record exists.
// 1. Check if a newly instantiated object has been saved to the database
employee = model("Employee").new(firstName="Jane", lastName="Doe");
if (employee.isNew()) {
// employee.save() has not been called yet, so no DB record exists
employee.save();
}
// 2. Check after loading from the database (isNew() returns false for persisted records)
employee = model("Employee").findOne(where="firstName='Jane'");
if (!employee.isNew()) {
// record already exists in the database
employee.firstName = "Janet";
employee.save();
}
// 3. Useful inside a before/after callback to branch logic for new vs. existing records
// In Employee.cfc:
component extends="Model" {
function config() {
beforeSave("stampAuditFields");
}
private function stampAuditFields() {
if (isNew()) {
this.createdBy = request.currentUserId;
}
this.updatedBy = request.currentUserId;
}
}
Returns whether the request was an OPTIONS request or not.
// 1. Respond to a CORS preflight OPTIONS request
if (isOptions()) {
header(name = "Access-Control-Allow-Methods", value = "GET, POST, PUT, DELETE");
renderNothing();
return;
}
// 2. Restrict an action to only handle OPTIONS requests
if (!isOptions()) {
redirectTo(action = "index");
}
// 3. Store the result to use in a conditional
requestIsOptions = isOptions();
// requestIsOptions -> true when the HTTP method is OPTIONS, false otherwise
Returns whether the request was a PATCH request or not.
// 1. Only handle PATCH requests in an action
if (!isPatch()) {
redirectTo(action = "index");
}
// 2. Respond differently depending on whether the request is a PATCH
if (isPatch()) {
// Apply a partial update to the resource
user = model("User").findByKey(params.key);
user.update(params.user);
} else {
// Not a PATCH request; redirect away
redirectTo(action = "index");
}
// 3. Store the result to use in a conditional
requestIsPatch = isPatch();
// requestIsPatch -> true for a PATCH request, false otherwise
Returns true if this object has been persisted to the database or was loaded from the database via a finder.
Returns false if the record has not been persisted to the database.
// 1. Check if a newly created (unsaved) object has been persisted
user = model("User").new(firstName="Jane", lastName="Doe");
writeOutput(user.isPersisted()); // -> false
// 2. Check if an object loaded from the database is persisted
user = model("User").findByKey(1);
writeOutput(user.isPersisted()); // -> true
// 3. Check persistence after saving a new object
post = model("Post").new(title="Hello World", body="First post.");
post.save();
writeOutput(post.isPersisted()); // -> true
Returns whether the request came from a form POST submission or not.
// 1. Respond differently depending on whether the request is a POST
if (isPost()) {
// Process the submitted form data
user = model("User").new(params.user);
if (user.save()) {
redirectTo(action="index");
} else {
renderView(action="new");
}
} else {
renderView(action="new");
}
// 2. Guard an action so it only accepts POST requests
function create() {
if (!isPost()) {
renderNothing(status="405 Method Not Allowed");
return;
}
// handle form submission
}
// 3. Store the result for later use in the action
requestIsPost = isPost();
// requestIsPost -> true (when submitted via a form POST)
// requestIsPost -> false (when the page is visited normally with GET)
Returns whether the request was a PUT request or not.
// 1. Allow only PUT requests for a resource update action
if (!isPut()) {
redirectTo(action = "index");
}
// ... proceed with update logic
// 2. Branch behavior based on HTTP method
if (isPut()) {
// Full replacement of the resource
user = model("User").findByKey(params.key);
user.update(params.user);
redirectTo(action = "show", key = user.key());
} else {
renderNothing();
}
Returns whether Wheels is communicating over a secure port.
X-Forwarded-Proto is client-controlled and is only honored when the app has opted into
proxy trust via set(trustProxyHeaders=true) behind a trusted reverse proxy.
// 1. Redirect non-secure connections to the HTTPS version
if (!isSecure()) {
redirectTo(protocol="https");
}
// 2. Conditionally set a secure cookie flag based on the connection
cookieOptions = {secure: isSecure(), httpOnly: true};
// 3. Log a warning when a sensitive action is performed over a non-secure connection
if (!isSecure()) {
logMessage("WARNING: sensitive action performed over non-secure connection");
}
isSSERequest()
boolean
controller
Check if the current request is from an EventSource client.
Useful for conditionally rendering SSE vs HTML responses.
Returns a script tag for a JavaScript file (or several) based on the supplied arguments.
Name
Type
Required
Default
Description
sources
string
No
The name of one or many JavaScript files in the javascripts folder, minus the .js extension. Pass a full URL to access an external JavaScript file. Can also be called with the source argument.
type
string
No
text/javascript
The type attribute for the script tag.
head
boolean
No
false
Set to true to place the output in the head area of the HTML page instead of the default behavior (which is to place the output where the function is called from).
delim
string
No
,
The delimiter to use for the list of JavaScript files.
encode
boolean
No
true
Use this argument to decide whether the output of the function should be encoded in order to prevent Cross Site Scripting (XSS) attacks. Set it to true to encode all relevant output for the specific HTML element in question (e.g. tag content, attribute values, and URLs). For HTML elements that have both tag content and attribute values you can set this argument to attributes to only encode attribute values and not tag content.
<!--- view code --->
<head>
<!--- Includes `javascripts/main.js` --->
#javaScriptIncludeTag("main")#
<!--- Includes `javascripts/blog.js` and `javascripts/accordion.js` --->
#javaScriptIncludeTag("blog,accordion")#
<!--- Includes an external JavaScript file --->
#javaScriptIncludeTag("https://ajax.googleapis.com/ajax/libs/jquery/3.7.1/jquery.min.js")#
<!--- Uses a pipe delimiter instead of the default comma --->
#javaScriptIncludeTag(sources="app|utils|vendor", delim="|")#
</head>
<body>
<!--- Will still appear in the `head` --->
#javaScriptIncludeTag(source="tabs", head=true)#
</body>
Returns the value of the primary key for the object.
If you have a single primary key named id, then someObject.key() is functionally equivalent to someObject.id.
This method is more useful when you do dynamic programming and don't know the name of the primary key or when you use composite keys (in which case it's convenient to use this method to get a list of both key values returned).
Name
Type
Required
Default
Description
// 1. Get the primary key value of a found object
employee = model("Employee").findByKey(params.key);
writeOutput(employee.key());
// -> 42
// 2. Use key() when you don't know the primary key column name (dynamic programming)
obj = model(params.modelName).findByKey(params.id);
if (IsObject(obj)) {
writeOutput("Found record with key: " & obj.key());
}
// 3. Composite primary key — key() returns a comma-delimited list of both values
orderItem = model("OrderItem").findByKey(key="1,5");
writeOutput(orderItem.key());
// -> 1,5 (orderId and productId combined)
Creates a link to the last page, or a disabled span when already on the last page.
Name
Type
Required
Default
Description
text
string
No
Last
The text for the link.
handle
string
No
query
The handle given to the query that the pagination should be displayed for.
name
string
No
page
The name of the param that holds the current page number.
class
string
No
CSS class for the link element.
disabledClass
string
No
disabled
CSS class for the disabled span element.
showDisabled
boolean
No
true
Whether to render a disabled span when already on the last page.
pageNumberAsParam
boolean
No
true
Decides whether to link the page number as a param or as part of a route.
encode
any
No
true
Use this argument to decide whether the output of the function should be encoded in order to prevent Cross Site Scripting (XSS) attacks. Set it to true to encode all relevant output for the specific HTML element in question (e.g. tag content, attribute values, and URLs). For HTML elements that have both tag content and attribute values you can set this argument to attributes to only encode attribute values and not tag content.
//--------------------------------------------------------------------
// Example 1: Basic usage — show a "Last" link at the bottom of a
// paginated list; renders a disabled span when already on the last page
// Controller code
param name="params.page" type="integer" default="1";
posts = model("Post").findAll(page=params.page, perPage=10, order="createdAt DESC");
// View code
<cfoutput>
#firstPageLink()#
#previousPageLink()#
#pageNumberLinks()#
#nextPageLink()#
#lastPageLink()#
</cfoutput>
//--------------------------------------------------------------------
// Example 2: Custom link text and CSS classes
// View code
<cfoutput>
#lastPageLink(
text="Last »»",
class="page-link",
disabledClass="page-link disabled"
)#
</cfoutput>
//--------------------------------------------------------------------
// Example 3: Hide the disabled element entirely when on the last page
// View code
<cfoutput>
#lastPageLink(showDisabled=false)#
</cfoutput>
//--------------------------------------------------------------------
// Example 4: Use a named route so page numbers appear in the URL path
// instead of as a query-string param (e.g. /articles/page/3)
// Route setup in app/config/routes.cfm
mapper()
.get(name="paginatedArticles", pattern="articles/page/[page]", to="articles##index")
.get(name="articles", pattern="articles", to="articles##index")
.end();
// Controller code
param name="params.page" type="integer" default="1";
articles = model("Article").findAll(page=params.page, perPage=20, order="title");
// View code
<cfoutput>
#lastPageLink(route="paginatedArticles", pageNumberAsParam=false)#
</cfoutput>
Creates a link to another page in your application.
Pass in the name of a route to use your configured routes or a controller/action/key combination.
Note: Pass any additional arguments like class, rel, and id, and the generated tag will also include those values as HTML attributes.
Name
Type
Required
Default
Description
text
string
No
The text content of the link.
route
string
No
Name of a route that you have configured in config/routes.cfm.
controller
string
No
Name of the controller to include in the URL.
action
string
No
Name of the action to include in the URL.
key
any
No
Key(s) to include in the URL.
params
string
No
Any additional parameters to be set in the query string (example: wheels=cool&x=y). Please note that Wheels uses the & and = characters to split the parameters and encode them properly for you. However, if you need to pass in & or = as part of the value, then you need to encode them (and only them), example: a=cats%26dogs%3Dtrouble!&b=1.
anchor
string
No
Sets an anchor name to be appended to the path.
onlyPath
boolean
No
true
If true, returns only the relative URL (no protocol, host name or port).
host
string
No
Set this to override the current host.
protocol
string
No
Set this to override the current protocol.
port
numeric
No
0
Set this to override the current port number.
href
string
No
Pass a link to an external site here if you want to bypass the Wheels routing system altogether and link to an external URL.
encode
any
No
true
Use this argument to decide whether the output of the function should be encoded in order to prevent Cross Site Scripting (XSS) attacks. Set it to true to encode all relevant output for the specific HTML element in question (e.g. tag content, attribute values, and URLs). For HTML elements that have both tag content and attribute values you can set this argument to attributes to only encode attribute values and not tag content.
// 1. Link to a controller/action pair
writeOutput(linkTo(text="Log Out", controller="account", action="logout"));
// -> <a href="/account/logout">Log Out</a>
// 2. Omit the controller when linking within the same controller
// (CFWheels uses the current controller automatically)
writeOutput(linkTo(text="Log Out", action="logout"));
// -> <a href="/account/logout">Log Out</a>
// 3. Link to a specific record using a key
writeOutput(linkTo(text="View Post", controller="blog", action="post", key=99));
// -> <a href="/blog/post/99">View Post</a>
// 4. Pass extra query string parameters
writeOutput(linkTo(text="View Settings", action="settings", params="show=all&sort=asc"));
// -> <a href="/account/settings?show=all&sort=asc">View Settings</a>
// 5. Use a named route (configured in app/config/routes.cfm)
writeOutput(linkTo(text="Joe's Profile", route="userProfile", userName="joe"));
// -> <a href="/user/joe">Joe's Profile</a>
// 6. Link to an external URL, bypassing the routing system
writeOutput(linkTo(text="ColdFusion on Wheels", href="https://cfwheels.org/"));
// -> <a href="https://cfwheels.org/">ColdFusion on Wheels</a>
// 7. Add HTML attributes (class, id, rel, etc.) via extra arguments
writeOutput(linkTo(text="Delete Post", action="delete", key=99, class="delete", id="delete-99"));
// -> <a class="delete" href="/blog/delete/99" id="delete-99">Delete Post</a>
// 8. Include icon markup in link text; use encode="attributes" to encode
// only attribute values and leave the tag content (the icon HTML) untouched
writeOutput(linkTo(text="<i class='fa fa-trash'></i> Delete Post", encode="attributes", action="delete", key=99));
// -> <a href="/blog/delete/99"><i class='fa fa-trash'></i> Delete Post</a>
// 9. Build an absolute URL by turning off onlyPath and setting a protocol/host
writeOutput(linkTo(text="Home", action="index", onlyPath=false, protocol="https", host="www.example.com"));
// -> <a href="https://www.example.com/home/index">Home</a>
// 10. Link to an anchor on the target page
writeOutput(linkTo(text="Jump to Comments", controller="blog", action="post", key=99, anchor="comments"));
// -> <a href="/blog/post/99#comments">Jump to Comments</a>
Creates a mailto link tag to the specified email address, which is also used as the name of the link unless name is specified.
Name
Type
Required
Default
Description
emailAddress
string
Yes
The email address to link to.
name
string
No
A string to use as the link text ("Joe" or "Support Department", for example).
encode
any
No
true
Use this argument to decide whether the output of the function should be encoded in order to prevent Cross Site Scripting (XSS) attacks. Set it to true to encode all relevant output for the specific HTML element in question (e.g. tag content, attribute values, and URLs). For HTML elements that have both tag content and attribute values you can set this argument to attributes to only encode attribute values and not tag content.
// 1. Basic mailto link using the email address as the link text
mailTo(emailAddress="webmaster@example.com");
// -> <a href="mailto:webmaster@example.com">webmaster@example.com</a>
// 2. Mailto link with a custom display name
mailTo(emailAddress="support@example.com", name="Contact Support");
// -> <a href="mailto:support@example.com">Contact Support</a>
// 3. Mailto link with additional HTML attributes (class, title)
mailTo(emailAddress="info@example.com", name="Email Us", class="email-link", title="Send us a message");
// -> <a href="mailto:info@example.com" class="email-link" title="Send us a message">Email Us</a>
Returns the mapper object used to configure your application's routes. Usually you will use this method in config/routes.cfm to start chaining route mapping methods like resources, namespace, etc.
Name
Type
Required
Default
Description
restful
boolean
No
true
Whether to turn on RESTful routing or not. Not recommended to set. Will probably be removed in a future version of wheels, as RESTful routes are the default.
methods
boolean
No
[runtime expression]
If not RESTful, then specify allowed routes. Not recommended to set. Will probably be removed in a future version of wheels, as RESTful routes are the default.
mapFormat
boolean
No
true
This is useful for providing formats via URL like json, xml, pdf, etc. Set to false to disable automatic .[format] generation for resource based routes
Calculates the maximum value for a given property.
Uses the SQL function MAX.
If no records can be found to perform the calculation on you can use the ifNull argument to decide what should be returned.
Name
Type
Required
Default
Description
property
string
Yes
Name of the property to get the highest value for (must be a property of a numeric data type).
where
string
No
Maps to the WHERE clause of the query (or HAVING when necessary). The following operators are supported: =, !=, <>, <, <=, >, >=, LIKE, NOT LIKE, IN, NOT IN, IS NULL, IS NOT NULL, AND, and OR (note that the key words need to be written in upper case). You can also use parentheses to group statements. Nested queries not allowed. You do not need to specify the table name(s); Wheels will do that for you.
include
string
No
Associations that should be included in the query using INNER or LEFT OUTER joins (which join type that is used depends on how the association has been set up in your model). If all included associations are set on the current model, you can specify them in a list (e.g. department,addresses,emails). You can build more complex include strings by using parentheses when the association is set on an included model, like album(artist(genre)), for example. These complex include strings only work when returnAs is set to query though.
parameterize
any
No
true
ifNull
any
No
The value returned if no records are found. Common usage is to set this to 0 to make sure a numeric value is always returned instead of a blank string.
includeSoftDeletes
boolean
No
false
Set to true to include soft-deleted records in the queries that this method runs.
group
string
No
Maps to the GROUP BY clause of the query. You do not need to specify the table name(s); Wheels will do that for you.
// 1. Get the highest salary across all employees
highestSalary = model("employee").maximum("salary");
// 2. Get the highest salary for employees in a specific department
highestSalary = model("employee").maximum(property="salary", where="departmentId=#params.key#");
// 3. Return 0 instead of a blank string when no matching records are found
highestSalary = model("employee").maximum(property="salary", where="salary > #params.minSalary#", ifNull=0);
// 4. Get the highest salary per department (returns a query with departmentId and the maximum value)
salaryByDept = model("employee").maximum(property="salary", group="departmentId");
Scope routes within a nested resource which require use of the primary key as part of the URL pattern;
A member route will require an ID, because it acts on a member.
photos/1/preview is an example of a member route, because it acts on (and displays) a single object.
<cfscript>
// 1. Add a preview route acting on a single photo (GET /photos/1/preview)
mapper()
.resources(name="photos", nested=true)
.member()
.get("preview")
.end()
.end()
.end();
// 2. Add multiple member routes (GET /articles/1/publish, DELETE /articles/1/archive)
mapper()
.resources(name="articles", nested=true)
.member()
.get("publish")
.delete("archive")
.end()
.end()
.end();
</cfscript>
Runs a single specific migration's up() regardless of sequence order.
Used for out-of-sequence migrations that were created by other developers
and need to be applied individually without affecting the current version pointer.
Name
Type
Required
Default
Description
version
string
Yes
The version number of the specific migration to run
// 1. Run a specific migration by version number (out-of-sequence)
result = application.wheels.migrator.migrateIndividual("20240315120000");
// result -> "Running individual migration 20240315120000.
// -------- 20240315120000_add_status_to_orders --------------------
// "
// 2. Check the result string for errors or success messages
result = application.wheels.migrator.migrateIndividual("20240101000000");
if (FindNoCase("Error", result)) {
writeOutput("Migration failed: " & result);
} else if (FindNoCase("already been applied", result)) {
writeOutput("Skipped: migration was already applied.");
} else {
writeOutput("Migration applied successfully.");
}
// 3. Apply an individual colleague's migration without advancing the version pointer
// This is useful when a team member's migration has a lower timestamp than
// the current version but was not yet applied in your environment.
colVersion = "20231205083000";
result = application.wheels.migrator.migrateIndividual(colVersion);
writeOutput(result);
Migrates database to a specified version. Whilst you can use this in your application, the recommended usage is via either the CLI or the provided GUI interface
Name
Type
Required
Default
Description
version
string
No
The Database schema version to migrate to
missingMigFlag
boolean
No
false
Flag for any available missing migrations
// 1. Migrate up to a specific version
result = application.wheels.migrator.migrateTo("20240315120000");
// result -> "Migrating from 20231201000000 up to 20240315120000.
// -------- 20240315120000_add_status_to_orders --------------------
// "
// 2. Migrate down to an earlier version (rolls back newer migrations)
result = application.wheels.migrator.migrateTo("20230601000000");
// result -> "Migrating from 20240315120000 down to 20230601000000.
// ------- 20240315120000_add_status_to_orders ---------------------
// "
// 3. Migrate to version 0 (rolls back all migrations)
result = application.wheels.migrator.migrateTo("0");
// result -> "Migrating from 20240315120000 down to 0.
// ..."
// 4. Check the result string for errors before proceeding
result = application.wheels.migrator.migrateTo("20240315120000");
if (FindNoCase("Error", result)) {
writeOutput("Migration failed: " & result);
} else {
writeOutput("Migration result: " & result);
}
// 5. Apply a missing (out-of-order gap) migration using missingMigFlag
// Use this when a migration with a timestamp earlier than the current version
// was never applied in your environment.
result = application.wheels.migrator.migrateTo(
version = "20231205083000",
missingMigFlag = true
);
writeOutput(result);
Shortcut function to migrate to the latest version
// 1. Run all pending migrations to bring the database up to the latest version
result = application.wheels.migrator.migrateToLatest();
// result -> "Migrating from 20240101000000 up to 20240315120000.
// -------- 20240315120000_add_status_to_orders --------------------
// "
// 2. Already at the latest version — no migration required
result = application.wheels.migrator.migrateToLatest();
// result -> "Database is currently at version 20240315120000. No migration required."
// 3. Check for errors after migrating to latest
result = application.wheels.migrator.migrateToLatest();
if (FindNoCase("Error", result)) {
writeOutput("Migration failed: " & result);
} else {
writeOutput("All migrations applied successfully.");
}
Returns an associated MIME type based on a file extension.
Name
Type
Required
Default
Description
extension
string
Yes
The extension to get the MIME type for.
fallback
string
No
application/octet-stream
The fallback MIME type to return.
// 1. Get the MIME type for a known file extension
mimeType = mimeTypes("xls");
// mimeType -> "application/vnd.ms-excel"
// 2. Get the MIME type for a dynamic extension from user input, with a custom fallback
mimeType = mimeTypes(extension=params.fileType, fallback="text/plain");
// 3. Use the default fallback (application/octet-stream) for an unknown extension
mimeType = mimeTypes("xyz");
// mimeType -> "application/octet-stream"
Calculates the minimum value for a given property.
Uses the SQL function MIN.
If no records can be found to perform the calculation on you can use the ifNull argument to decide what should be returned.
Name
Type
Required
Default
Description
property
string
Yes
Name of the property to get the lowest value for (must be a property of a numeric data type).
where
string
No
Maps to the WHERE clause of the query (or HAVING when necessary). The following operators are supported: =, !=, <>, <, <=, >, >=, LIKE, NOT LIKE, IN, NOT IN, IS NULL, IS NOT NULL, AND, and OR (note that the key words need to be written in upper case). You can also use parentheses to group statements. Nested queries not allowed. You do not need to specify the table name(s); Wheels will do that for you.
include
string
No
Associations that should be included in the query using INNER or LEFT OUTER joins (which join type that is used depends on how the association has been set up in your model). If all included associations are set on the current model, you can specify them in a list (e.g. department,addresses,emails). You can build more complex include strings by using parentheses when the association is set on an included model, like album(artist(genre)), for example. These complex include strings only work when returnAs is set to query though.
parameterize
any
No
true
Set to true to use cfqueryparam on all columns, or pass in a list of property names to use cfqueryparam on those only.
ifNull
any
No
The value returned if no records are found. Common usage is to set this to 0 to make sure a numeric value is always returned instead of a blank string.
includeSoftDeletes
boolean
No
false
Set to true to include soft-deleted records in the queries that this method runs.
group
string
No
Maps to the GROUP BY clause of the query. You do not need to specify the table name(s); Wheels will do that for you.
// 1. Get the amount of the lowest salary for all employees
lowestSalary = model("employee").minimum("salary");
// 2. Get the amount of the lowest salary for employees in a given department
lowestSalary = model("employee").minimum(property="salary", where="departmentId=#params.key#");
// 3. Make sure a numeric amount is always returned, even when there were no records analyzed by the query
lowestSalary = model("employee").minimum(property="salary", where="salary BETWEEN #params.min# AND #params.max#", ifNull=0);
Builds and returns a string containing one select form control for the minutes of an hour based on the supplied name.
Name
Type
Required
Default
Description
name
string
Yes
Name to populate in tag's name attribute.
selected
string
No
The minute that should be selected initially.
minuteStep
numeric
No
1
Pass in 10 to only show minute 10, 20, 30, etc.
includeBlank
any
No
false
Whether to include a blank option in the select form control. Pass true to include a blank line or a string that should represent what display text should appear for the empty value (for example, "- Select One -").
label
string
No
The label text to use in the form control.
labelPlacement
string
No
around
Whether to place the label before, after, or wrapped around the form control. Label text placement can be controlled using aroundLeft or aroundRight.
prepend
string
No
String to prepend to the form control. Useful to wrap the form control with HTML tags.
append
string
No
String to append to the form control. Useful to wrap the form control with HTML tags.
prependToLabel
string
No
String to prepend to the form control's label. Useful to wrap the form control with HTML tags.
appendToLabel
string
No
String to append to the form control's label. Useful to wrap the form control with HTML tags.
encode
any
No
true
Use this argument to decide whether the output of the function should be encoded in order to prevent Cross Site Scripting (XSS) attacks. Set it to true to encode all relevant output for the specific HTML element in question (e.g. tag content, attribute values, and URLs). For HTML elements that have both tag content and attribute values you can set this argument to attributes to only encode attribute values and not tag content.
// 1. Basic usage — the "Tag" version accepts a `name` and `selected` instead of binding to a model object
<cfoutput>
#minuteSelectTag(name="minuteOfMeeting", selected=params.minuteOfMeeting)#
</cfoutput>
// 2. Only show 15-minute intervals
<cfoutput>
#minuteSelectTag(name="minuteOfMeeting", selected=params.minuteOfMeeting, minuteStep=15)#
</cfoutput>
// 3. Include a blank option and add a label
<cfoutput>
#minuteSelectTag(name="minuteOfMeeting", selected=params.minuteOfMeeting, includeBlank=true, label="Minute")#
</cfoutput>
Returns a reference to the requested model so that class level methods can be called on it.
Name
Type
Required
Default
Description
name
string
Yes
Name of the model to get a reference to.
// 1. Get a reference to the User model and call a class-level finder on it
user = model("User").findByKey(params.key);
// 2. Find all active users by calling findAll() on the model reference
activeUsers = model("User").findAll(where="active = 1", order="lastName");
// 3. Create a new record via the model reference
newPost = model("Post").new(title=params.title, body=params.body);
newPost.save();
// 4. Count records using the model reference
totalOrders = model("Order").count(where="status = 'pending'");
Whether to include a blank option in the select form control. Pass true to include a blank line or a string that should represent what display text should appear for the empty value (for example, "- Select One -").
label
string
No
The label text to use in the form control.
labelPlacement
string
No
around
Whether to place the label before, after, or wrapped around the form control. Label text placement can be controlled using aroundLeft or aroundRight.
prepend
string
No
String to prepend to the form control. Useful to wrap the form control with HTML tags.
append
string
No
String to append to the form control. Useful to wrap the form control with HTML tags.
prependToLabel
string
No
String to prepend to the form control's label. Useful to wrap the form control with HTML tags.
appendToLabel
string
No
String to append to the form control's label. Useful to wrap the form control with HTML tags.
encode
any
No
true
Use this argument to decide whether the output of the function should be encoded in order to prevent Cross Site Scripting (XSS) attacks. Set it to true to encode all relevant output for the specific HTML element in question (e.g. tag content, attribute values, and URLs). For HTML elements that have both tag content and attribute values you can set this argument to attributes to only encode attribute values and not tag content.
// 1. Basic usage: render a month select tag bound to a form param
<cfoutput>
#monthSelectTag(name="monthOfBirthday", selected=params.monthOfBirthday)#
</cfoutput>
// 2. Display month abbreviations instead of full names and include a blank prompt
<cfoutput>
#monthSelectTag(
name="month",
selected=params.month,
monthDisplay="abbreviations",
includeBlank="- Select Month -"
)#
</cfoutput>
// 3. Display month numbers with a label wrapped around the control
<cfoutput>
#monthSelectTag(
name="expirationMonth",
selected=params.expirationMonth,
monthDisplay="numbers",
label="Expiration Month"
)#
</cfoutput>
Allows for nested objects, structs, and arrays to be set from params and other generated data.
Name
Type
Required
Default
Description
association
string
No
The association (or list of associations) you want to allow to be set through the params. This argument is also aliased as associations.
autoSave
boolean
No
true
Whether to save the association(s) when the parent object is saved.
allowDelete
boolean
No
false
Set this to true to tell Wheels to look for the property _delete in your model. If present and set to a value that evaluates to true, the model will be deleted when saving the parent.
sortProperty
string
No
Set this to a property on the object that you would like to sort by. The property should be numeric, should start with 1, and should be consecutive. Only valid with hasMany associations.
rejectIfBlank
string
No
A list of properties that should not be blank. If any of the properties are blank, any CRUD operations will be rejected.
// 1. In `models/User.cfc`, allow `groupEntitlements` to be saved and deleted through the `user` object.
function config() {
hasMany("groupEntitlements");
nestedProperties(association="groupEntitlements", allowDelete=true);
}
// 2. Allow nested `addresses` to be saved but not auto-saved with the parent; also reject blank `street` values.
function config() {
hasMany("addresses");
nestedProperties(association="addresses", autoSave=false, rejectIfBlank="street");
}
// 3. Allow nested `lineItems` with a sort order driven by the `position` property, and enable deletion.
function config() {
hasMany("lineItems");
nestedProperties(association="lineItems", allowDelete=true, sortProperty="position");
}
// 4. Enable nested properties for multiple associations at once.
function config() {
hasOne("profile");
hasMany("phoneNumbers");
nestedProperties(associations="profile,phoneNumbers");
}
Creates a new object based on supplied properties and returns it.
The object is not saved to the database, it only exists in memory.
Property names and values can be passed in either using named arguments or as a struct to the properties argument.
Name
Type
Required
Default
Description
properties
struct
No
[runtime expression]
The properties you want to set on the object (can also be passed in as named arguments).
callbacks
boolean
No
true
Set to false to disable callbacks for this method.
allowExplicitTimestamps
boolean
No
false
Set this to true to allow explicit assignment of createdAt or updatedAt properties
// 1. Create a new author in memory (not saved to the database)
newAuthor = model("author").new();
// 2. Create a new author by passing in a struct of properties
newAuthor = model("author").new(params.authorStruct);
// 3. Create a new author by passing in named arguments
newAuthor = model("author").new(firstName="John", lastName="Doe");
// 4. Create a new object without running callbacks
newAuthor = model("author").new(firstName="Jane", callbacks=false);
// 5. Create a new object and allow explicit assignment of timestamp properties
newAuthor = model("author").new(firstName="Bob", createdAt="2024-01-01", allowExplicitTimestamps=true);
// 6. Scoped call via a `hasMany` association (calls `model("order").new(customerId=aCustomer.id)` internally)
aCustomer = model("customer").findByKey(params.customerId);
anOrder = aCustomer.newOrder(shipping=params.shipping);
Creates a link to the next page, or a disabled span when on the last page.
Name
Type
Required
Default
Description
text
string
No
Next
The text for the link.
handle
string
No
query
The handle given to the query that the pagination should be displayed for.
name
string
No
page
The name of the param that holds the current page number.
class
string
No
CSS class for the link element.
disabledClass
string
No
disabled
CSS class for the disabled span element.
showDisabled
boolean
No
true
Whether to render a disabled span when on the last page.
pageNumberAsParam
boolean
No
true
Decides whether to link the page number as a param or as part of a route.
encode
any
No
true
Use this argument to decide whether the output of the function should be encoded in order to prevent Cross Site Scripting (XSS) attacks. Set it to true to encode all relevant output for the specific HTML element in question (e.g. tag content, attribute values, and URLs). For HTML elements that have both tag content and attribute values you can set this argument to attributes to only encode attribute values and not tag content.
//--------------------------------------------------------------------
// Example 1: Basic usage — show a "Next" link below a paginated list;
// renders a disabled span when already on the last page
// Controller code
param name="params.page" type="integer" default="1";
posts = model("Post").findAll(page=params.page, perPage=10, order="createdAt DESC");
// View code
<cfoutput>
#firstPageLink()#
#previousPageLink()#
#pageNumberLinks()#
#nextPageLink()#
#lastPageLink()#
</cfoutput>
//--------------------------------------------------------------------
// Example 2: Custom link text and CSS classes
// View code
<cfoutput>
#nextPageLink(
text="Next »",
class="page-link",
disabledClass="page-link disabled"
)#
</cfoutput>
//--------------------------------------------------------------------
// Example 3: Hide the disabled element entirely when on the last page
// View code
<cfoutput>
#nextPageLink(showDisabled=false)#
</cfoutput>
//--------------------------------------------------------------------
// Example 4: Use a named route so page numbers appear in the URL path
// instead of as a query-string param (e.g. /articles/page/3)
// Route setup in app/config/routes.cfm
mapper()
.get(name="paginatedArticles", pattern="articles/page/[page]", to="articles##index")
.get(name="articles", pattern="articles", to="articles##index")
.end();
// Controller code
param name="params.page" type="integer" default="1";
articles = model("Article").findAll(page=params.page, perPage=20, order="title");
// View code
<cfoutput>
#nextPageLink(route="paginatedArticles", pageNumberAsParam=false)#
</cfoutput>
Builds and returns a string containing a number field form control based on the supplied objectName and property.
Note: Pass any additional arguments like class, rel, and id, and the generated tag will also include those values as HTML attributes.
Name
Type
Required
Default
Description
objectName
any
Yes
The variable name of the object to build the form control for.
property
string
Yes
The name of the property to use in the form control.
association
string
No
The name of the association that the property is located on. Used for building nested forms that work with nested properties. If you are building a form with deep nesting, simply pass in a list to the nested object, and Wheels will figure it out.
position
string
No
The position used when referencing a hasMany relationship in the association argument. Used for building nested forms that work with nested properties. If you are building a form with deep nestings, simply pass in a list of positions, and Wheels will figure it out.
min
string
No
Minimum allowed value.
max
string
No
Maximum allowed value.
step
string
No
Stepping interval.
label
string
No
useDefaultLabel
The label text to use in the form control.
labelPlacement
string
No
around
Whether to place the label before, after, or wrapped around the form control. Label text placement can be controlled using aroundLeft or aroundRight.
prepend
string
No
String to prepend to the form control. Useful to wrap the form control with HTML tags.
append
string
No
String to append to the form control. Useful to wrap the form control with HTML tags.
prependToLabel
string
No
String to prepend to the form control's label. Useful to wrap the form control with HTML tags.
appendToLabel
string
No
String to append to the form control's label. Useful to wrap the form control with HTML tags.
errorElement
string
No
span
HTML tag to wrap the form control with when the object contains errors.
errorClass
string
No
field-with-errors
The class name of the HTML tag that wraps the form control when there are errors.
encode
any
No
true
Use this argument to decide whether the output of the function should be encoded in order to prevent Cross Site Scripting (XSS) attacks. Set it to true to encode all relevant output for the specific HTML element in question (e.g. tag content, attribute values, and URLs). For HTML elements that have both tag content and attribute values you can set this argument to attributes to only encode attribute values and not tag content.
// 1. Basic number field bound to a model object
#numberField(objectName="product", property="price")#
// 2. Number field with a label and min/max/step constraints
#numberField(label="Quantity", objectName="orderItem", property="quantity", min="1", max="100", step="1")#
// 3. Number field for a nested association (line items in an order)
<cfloop from="1" to="#ArrayLen(order.lineItems)#" index="i">
#numberField(label="Amount ##i#", objectName="order", association="lineItems", position=i, property="amount", min="0")#
</cfloop>
Builds and returns a string containing a number field form control based on the supplied name.
Note: Pass any additional arguments like class, rel, and id, and the generated tag will also include those values as HTML attributes.
Name
Type
Required
Default
Description
name
string
Yes
Name to populate in tag's name attribute.
value
string
No
Value to populate in tag's value attribute.
min
string
No
Minimum allowed value.
max
string
No
Maximum allowed value.
step
string
No
Stepping interval.
label
string
No
The label text to use in the form control.
labelPlacement
string
No
around
Whether to place the label before, after, or wrapped around the form control. Label text placement can be controlled using aroundLeft or aroundRight.
prepend
string
No
String to prepend to the form control. Useful to wrap the form control with HTML tags.
append
string
No
String to append to the form control. Useful to wrap the form control with HTML tags.
prependToLabel
string
No
String to prepend to the form control's label. Useful to wrap the form control with HTML tags.
appendToLabel
string
No
String to append to the form control's label. Useful to wrap the form control with HTML tags.
encode
any
No
true
Use this argument to decide whether the output of the function should be encoded in order to prevent Cross Site Scripting (XSS) attacks. Set it to true to encode all relevant output for the specific HTML element in question (e.g. tag content, attribute values, and URLs). For HTML elements that have both tag content and attribute values you can set this argument to attributes to only encode attribute values and not tag content.
// 1. Basic number field with a name and current value
#numberFieldTag(name="quantity", value=params.quantity)#
// 2. Number field with min, max, and step constraints plus a label
#numberFieldTag(name="rating", value=params.rating, label="Rating", min="1", max="5", step="1")#
// 3. Number field with a CSS class and prepended label text
#numberFieldTag(name="price", value=params.price, label="Price ($)", min="0", step="0.01", class="price-input")#
Obfuscates a value. Typically used for hiding primary key values when passed along in the URL.
Name
Type
Required
Default
Description
param
any
Yes
The value to obfuscate.
// 1. Obfuscate a primary key value before including it in a URL
obfuscatedId = obfuscateParam(99);
// obfuscatedId -> "a3f6c1" (an obfuscated hex string)
// 2. Use an obfuscated key in a generated URL to hide the real record ID
params.userKey = obfuscateParam(model("User").findOne().key());
redirectTo(route="userProfile", key=params.userKey);
// 3. Reverse the obfuscation with deobfuscateParam to get the original value back
obfuscated = obfuscateParam(42);
original = deobfuscateParam(obfuscated);
// original -> "42"
Use this in an individual controller action to define which formats the action will respond with.
This can be used to define provides behavior in individual actions or to override a global setting set with provides in the controller's config().
Restrictions are enforced (since 4.0.4): renderWith() falls back to the html view for a
format outside the list, and the automatic render in $callAction() skips view rendering for
non-acceptable, non-html formats.
Name
Type
Required
Default
Description
formats
string
No
Formats to instruct the controller to provide. Valid values are html (the default), xml, json, csv, pdf, and xls.
action
string
No
[runtime expression]
Name of action, defaults to current.
// 1. Override a global `provides()` setting for a single action — only respond with HTML
onlyProvides("html");
// 2. Allow a specific action to respond with JSON and XML only
onlyProvides("json,xml");
// 3. Override the provides formats for a named action from within another context (e.g. config())
onlyProvides(formats="json", action="create");
This method is not designed to be called directly from your code, but provides functionality for dynamic finders such as findOneByEmail()
Name
Type
Required
Default
Description
missingMethodName
string
Yes
missingMethodArguments
struct
Yes
// Note: onMissingMethod() is not called directly. It is the CFML hook that
// powers Wheels' dynamic model methods. The examples below show what you
// call in your code — Wheels intercepts each one automatically.
// 1. Dynamic finder: findOneBy<Property>
// Finds the first user whose email matches the given value.
user = model("User").findOneByEmail("jane@example.com");
// 2. Dynamic finder: findAllBy<Property>
// Finds all posts with the given status.
posts = model("Post").findAllByStatus("published");
// 3. Dynamic finder across multiple properties joined by "And"
// Finds a single order matching both customerId and status.
order = model("Order").findOneByCustomerIdAndStatus(42, "pending");
// 4. Find or create by property
// Returns an existing tag with the name "cfml", or creates one if none exists.
tag = model("Tag").findOrCreateByName("cfml");
// 5. Association helpers generated for hasMany (comments on a post)
post = model("Post").findByKey(1);
// Retrieve all associated comments
comments = post.comments();
// Count associated comments
total = post.commentCount();
// Create a new associated comment (foreign key set automatically)
post.createComment(body="Great post!");
// 6. Property change helpers
user = model("User").findByKey(1);
user.email = "new@example.com";
// Check whether a specific property has changed since the record was loaded
changed = user.emailHasChanged();
// Get the original value before the change
original = user.emailChangedFrom();
// 7. Enum boolean helpers (requires enum() declaration in model config)
// component Post extends="Model" { function config() { enum(property="status", values="draft,published,archived"); } }
post = model("Post").findByKey(1);
writeOutput(post.isPublished()); // true or false
writeOutput(post.isDraft()); // true or false
Scopes any the controllers for any routes configured within this block to a subfolder (package) without adding the package name to the URL.
Name
Type
Required
Default
Description
name
string
Yes
Name to prepend to child route names.
package
string
No
[runtime expression]
Subfolder (package) to reference for controllers. This defaults to the value provided for name.
<cfscript>
// 1. Scope controllers into a subfolder without adding the package name to the URL
mapper()
.package("admin")
// Route name: adminProducts
// Example URL: /products (no "admin" in the URL)
// Controller: admin.Products
.resources("products")
// Example URL: /users (no "admin" in the URL)
// Controller: admin.Users
.resources("users")
.end()
.end();
// 2. Use the `package` argument to override the subfolder name
mapper()
.package(name="v2", package="api/v2")
// Route name: v2Articles
// Example URL: /articles
// Controller: api/v2.Articles
.resources("articles")
.end()
.end();
// 3. Nest a `package` inside a resource to scope sub-resource controllers
mapper()
.resources(name="users", nested=true)
// Calling `package` here scopes nested routes to a subfolder without
// changing the URL structure.
.package("users")
// Route name: usersProfile
// Example URL: /users/4321/profile
// Controller: users.Profiles
.resource("profile")
.end()
.end()
.end();
</cfscript>
Creates a windowed set of page number links around the current page.
The current page is rendered as a span (not a link) unless linkToCurrentPage is true.
When non-plain, emits the canonical wrapper markup for that framework (e.g. )
and ignores prependToPage / appendToPage / classForCurrent / class in favor of the preset.
Name
Type
Required
Default
Description
windowSize
numeric
No
2
The number of page links to show around the current page.
handle
string
No
query
The handle given to the query that the pagination should be displayed for.
name
string
No
page
The name of the param that holds the current page number.
class
string
No
CSS class for each page number link.
classForCurrent
string
No
current
CSS class for the current page span or link.
linkToCurrentPage
boolean
No
false
Whether to render the current page as a link.
prependToPage
string
No
String to prepend before each page number.
appendToPage
string
No
String to append after each page number.
addActiveClassToPrependedParent
boolean
No
false
Whether to inject active into the prependToPage class attribute on the current page (Bootstrap idiom). Has no effect if prependToPage contains no class attribute.
pageNumberAsParam
boolean
No
true
Decides whether to link the page number as a param or as part of a route.
viewStyle
string
No
plain
CSS-framework preset for markup: "plain" (default), "bootstrap5", "bootstrap4", or "tailwind".
encode
any
No
true
Use this argument to decide whether the output of the function should be encoded in order to prevent Cross Site Scripting (XSS) attacks. Set it to true to encode all relevant output for the specific HTML element in question (e.g. tag content, attribute values, and URLs). For HTML elements that have both tag content and attribute values you can set this argument to attributes to only encode attribute values and not tag content.
//--------------------------------------------------------------------
// Example 1: Basic page number links for a paginated query
// Controller code
param name="params.page" type="integer" default="1";
posts = model("Post").findAll(page=params.page, perPage=10, order="createdAt DESC");
// View code — renders links like: 1 2 [3] 4 5 (current page as a span)
<cfoutput>#pageNumberLinks()#</cfoutput>
//--------------------------------------------------------------------
// Example 2: Widen the window around the current page and add CSS classes
// View code — shows 5 pages on each side of the current page,
// styling each link and the current-page span differently
<cfoutput>
#pageNumberLinks(windowSize=5, class="page-link", classForCurrent="active")#
</cfoutput>
//--------------------------------------------------------------------
// Example 3: Wrap each page number in a list item
// View code
<ul>
<cfoutput>
#pageNumberLinks(prependToPage="<li>", appendToPage="</li>")#
</cfoutput>
</ul>
//--------------------------------------------------------------------
// Example 4: Make the current page a link (useful for reloading)
// View code
<cfoutput>#pageNumberLinks(linkToCurrentPage=true)#</cfoutput>
//--------------------------------------------------------------------
// Example 5: Multiple paginated queries — reference each by its handle
// Controller code
authors = model("Author").findAll(handle="authorQuery", page=params.page, perPage=20, order="lastName");
posts = model("Post").findAll(handle="postQuery", page=params.page, perPage=5, order="createdAt");
// View code
<cfoutput>
Authors: #pageNumberLinks(handle="authorQuery")#
Posts: #pageNumberLinks(handle="postQuery")#
</cfoutput>
Returns a struct with information about the specified paginated query.
The keys that will be included in the struct are currentPage, totalPages and totalRecords.
Name
Type
Required
Default
Description
handle
string
No
query
The handle given to the query to return pagination information for.
// 1. Get pagination info for the default query handle
authors = model("Author").findAll(page=1, perPage=25, order="lastName");
info = pagination();
// info.currentPage -> 1
// info.totalPages -> 4
// info.totalRecords -> 98
// 2. Get pagination info using a named handle (when running multiple paginated queries)
articles = model("Article").findAll(page=2, perPage=10, order="publishedAt DESC", handle="articles");
articleInfo = pagination("articles");
writeOutput("Page " & articleInfo.currentPage & " of " & articleInfo.totalPages);
// 3. Use pagination info to build a simple summary string
products = model("Product").findAll(page=params.page, perPage=20, order="name", handle="products");
info = pagination("products");
writeOutput("Showing page " & info.currentPage & " of " & info.totalPages & " (" & info.totalRecords & " total products)");
Displays a text summary of the current pagination state, e.g. "Showing 26-50 of 1,000 records".
Uses token replacement in the format string:
Name
Type
Required
Default
Description
handle
string
No
query
The handle given to the query that the pagination info should be displayed for.
format
string
No
Showing [startRow]-[endRow] of [totalRecords] records
Format string with tokens: [startRow], [endRow], [totalRecords], [currentPage], [totalPages].
encode
any
No
true
Use this argument to decide whether the output of the function should be encoded in order to prevent Cross Site Scripting (XSS) attacks. Set it to true to encode all relevant output for the specific HTML element in question (e.g. tag content, attribute values, and URLs). For HTML elements that have both tag content and attribute values you can set this argument to attributes to only encode attribute values and not tag content.
//--------------------------------------------------------------------
// Example 1: Basic usage — display the default summary text for a
// paginated query (e.g. "Showing 26-50 of 1,000 records")
// Controller code
param name="params.page" type="integer" default="1";
posts = model("Post").findAll(page=params.page, perPage=25, order="createdAt DESC");
// View code
<cfoutput>
#paginationInfo()#
</cfoutput>
// -> "Showing 26-50 of 1,000 records"
//--------------------------------------------------------------------
// Example 2: Custom format string using available tokens
// Tokens: [startRow], [endRow], [totalRecords], [currentPage], [totalPages]
// View code
<cfoutput>
#paginationInfo(format="Page [currentPage] of [totalPages] ([totalRecords] total)")#
</cfoutput>
// -> "Page 2 of 40 (1,000 total)"
//--------------------------------------------------------------------
// Example 3: Multiple paginated queries on the same page using handles
// Controller code
param name="params.postPage" type="integer" default="1";
param name="params.commentPage" type="integer" default="1";
posts = model("Post").findAll(handle="posts", page=params.postPage, perPage=10, order="createdAt DESC");
comments = model("Comment").findAll(handle="comments", page=params.commentPage, perPage=5, order="createdAt DESC");
// View code
<cfoutput>
Posts: #paginationInfo(handle="posts")#
Comments: #paginationInfo(handle="comments")#
</cfoutput>
// -> "Posts: Showing 1-10 of 87 records"
// -> "Comments: Showing 1-5 of 342 records"
Builds and returns a string containing links to pages based on a paginated query.
Uses linkTo() internally to build the link, so you need to pass in a route name or a controller/action/key combination.
All other linkTo() arguments can be supplied as well, in which case they are passed through directly to linkTo().
If you have paginated more than one query in the controller, you can use the handle argument to reference them. (Don't forget to pass in a handle to the findAll() function in your controller first.)
Name
Type
Required
Default
Description
windowSize
numeric
No
2
The number of page links to show around the current page.
alwaysShowAnchors
boolean
No
true
Whether or not links to the first and last page should always be displayed.
anchorDivider
string
No
...
String to place next to the anchors on either side of the list.
linkToCurrentPage
boolean
No
false
Whether or not the current page should be linked to.
prepend
string
No
String or HTML to be prepended before result.
append
string
No
String or HTML to be appended after result.
prependToPage
string
No
String or HTML to be prepended before each page number.
addActiveClassToPrependedParent
boolean
No
false
Whether or not to add an active class to the parent element of the current page link (requires prependToPage to contain a class attribute).
prependOnFirst
boolean
No
true
Whether or not to prepend the prependToPage string on the first page in the list.
prependOnAnchor
boolean
No
true
Whether or not to prepend the prependToPage string on the anchors.
appendToPage
string
No
String or HTML to be appended after each page number.
appendOnLast
boolean
No
true
Whether or not to append the appendToPage string on the last page in the list.
appendOnAnchor
boolean
No
true
Whether or not to append the appendToPage string on the anchors.
classForCurrent
string
No
Class name for the current page number (if linkToCurrentPage is true, the class name will go on the a element. If not, a span element will be used).
handle
string
No
query
The handle given to the query that the pagination links should be displayed for.
name
string
No
page
The name of the param that holds the current page number.
showSinglePage
boolean
No
false
Will show a single page when set to true. (The default behavior is to return an empty string when there is only one page in the pagination).
pageNumberAsParam
boolean
No
true
Decides whether to link the page number as a param or as part of a route. (The default behavior is true).
encode
any
No
true
Use this argument to decide whether the output of the function should be encoded in order to prevent Cross Site Scripting (XSS) attacks. Set it to true to encode all relevant output for the specific HTML element in question (e.g. tag content, attribute values, and URLs). For HTML elements that have both tag content and attribute values you can set this argument to attributes to only encode attribute values and not tag content.
//--------------------------------------------------------------------
// Example 1: List authors page by page, 25 at a time
// Controller code
param name="params.page" type="integer" default="1";
authors = model("author").findAll(page=params.page, perPage=25, order="lastName");
// View code
<ul>
<cfoutput query="authors">
<li>#EncodeForHtml(firstName)# #EncodeForHtml(lastName)#</li>
</cfoutput>
</ul>
<cfoutput>#paginationLinks(route="authors")#</cfoutput>
//--------------------------------------------------------------------
// Example 2: Using the same model call above, show all authors with a
// window size of 5
// View code
<cfoutput>#paginationLinks(route="authors", windowSize=5)#</cfoutput>
//--------------------------------------------------------------------
// Example 3: If more than one paginated query is being run, then you
// need to reference the correct `handle` in the view
// Controller code
authors = model("author").findAll(handle="authQuery", page=5, order="id");
// View code
<ul>
<cfoutput>
#paginationLinks(
route="authors",
handle="authQuery",
prependToPage="<li>",
appendToPage="</li>"
)#
</cfoutput>
</ul>
//--------------------------------------------------------------------
// Example 4: Call to `paginationLinks` using routes
// Route setup in app/config/routes.cfm
mapper()
.get(name="paginatedCommentListing", pattern="blog/[year]/[month]/[day]/[page]", to="blogs##stats")
.get(name="commentListing", pattern="blog/[year]/[month]/[day]", to="blogs##stats")
.end();
// Controller code
param name="params.page" type="integer" default="1";
comments = model("comment").findAll(page=params.page, order="createdAt");
// View code
<ul>
<cfoutput>
#paginationLinks(
route="paginatedCommentListing",
year=2009,
month="feb",
day=10
)#
</cfoutput>
</ul>
//--------------------------------------------------------------------
// Example 5: Highlight the current page with a CSS class and wrap
// each page number in a Bootstrap-style list item, marking the active
// item's parent with an "active" class
// View code
<ul class="pagination">
<cfoutput>
#paginationLinks(
route="articles",
prependToPage='<li class="page-item">',
appendToPage="</li>",
classForCurrent="page-link active",
addActiveClassToPrependedParent=true,
linkToCurrentPage=true
)#
</cfoutput>
</ul>
Creates a complete pagination navigation element wrapping individual pagination helpers.
Outputs a element containing first/previous/page-numbers/next/last links and optional info text.
The showFirst / showLast / showPrevious / showNext args accept the
strings "auto", "always", or "never". Booleans are normalized for
backwards compatibility: true maps to "always", false maps to "never".
Under "auto" the first/last anchors only render when the visible page-number
window does not already reach the boundary (matching legacy 3.x semantics).
Under "auto" the previous/next anchors always delegate to their sub-helper,
which renders a disabled at the boundary by default —
use "never" to suppress the boundary indicator entirely.
When non-plain, the entire nav is rendered with the framework's canonical structure
(e.g. ), removing the need
for Replace() post-processing in app code. Passed through to pageNumberLinks().
Name
Type
Required
Default
Description
handle
string
No
query
The handle given to the query that the pagination should be displayed for.
navClass
string
No
pagination
CSS class for the wrapping nav element.
showFirst
any
No
auto
Anchor display mode for the first page link: "auto" (default), "always", "never", or boolean.
showLast
any
No
auto
Anchor display mode for the last page link: "auto" (default), "always", "never", or boolean.
showPrevious
any
No
auto
Anchor display mode for the previous page link: "auto" (default), "always", "never", or boolean.
showNext
any
No
auto
Anchor display mode for the next page link: "auto" (default), "always", "never", or boolean.
showInfo
boolean
No
false
Whether to show the pagination info text.
showSinglePage
boolean
No
false
Whether to show pagination when there is only one page.
windowSize
numeric
No
2
Number of page links shown around the current page in pageNumberLinks and used by the auto-mode predicates.
viewStyle
string
No
plain
CSS-framework preset for markup: "plain" (default), "bootstrap5", "bootstrap4", or "tailwind".
prepend
string
No
String or HTML to be prepended inside the before the link list (e.g.
).
append
string
No
String or HTML to be appended inside the after the link list (e.g. ).
prependToPage
string
No
String or HTML to wrap before each anchor (first/previous/page numbers/next/last). Forwards to pageNumberLinks for the numbered links.
appendToPage
string
No
String or HTML to wrap after each anchor (first/previous/page numbers/next/last). Forwards to pageNumberLinks for the numbered links.
addActiveClassToPrependedParent
boolean
No
false
Whether to inject active into the prependToPage class attribute on the current page (Bootstrap idiom — forwards to pageNumberLinks). Applies only to numbered-page anchors, not to first / previous / next / last (which are never "current" in the Bootstrap sense). Has no effect if prependToPage contains no class attribute.
anchorDivider
string
No
Separator inserted between the first/previous/page-numbers/next/last sections.
encode
any
No
true
Use this argument to decide whether the output of the function should be encoded in order to prevent Cross Site Scripting (XSS) attacks. Set it to true to encode all relevant output for the specific HTML element in question (e.g. tag content, attribute values, and URLs). For HTML elements that have both tag content and attribute values you can set this argument to attributes to only encode attribute values and not tag content.
//--------------------------------------------------------------------
// Example 1: Basic usage — render a full pagination nav for a
// paginated query (first, previous, page numbers, next, last links)
// Controller code
param name="params.page" type="integer" default="1";
posts = model("Post").findAll(page=params.page, perPage=25, order="createdAt DESC");
// View code
<cfoutput>
#paginationNav()#
</cfoutput>
// -> <nav class="pagination"><a href="/posts?page=1">1</a> <a href="/posts?page=2">2</a> ...</nav>
//--------------------------------------------------------------------
// Example 2: Show pagination info text alongside the nav links,
// and use a custom CSS class on the wrapping nav element
// View code
<cfoutput>
#paginationNav(showInfo=true, navClass="pagination-bar")#
</cfoutput>
// -> <nav class="pagination-bar">Showing 1-25 of 87 records <a href="...">1</a> ...</nav>
//--------------------------------------------------------------------
// Example 3: Minimal nav — page numbers only (no first/last links)
// View code
<cfoutput>
#paginationNav(showFirst=false, showLast=false)#
</cfoutput>
//--------------------------------------------------------------------
// Example 4: Multiple paginated queries on the same page using handles
// Controller code
param name="params.postPage" type="integer" default="1";
param name="params.commentPage" type="integer" default="1";
posts = model("Post").findAll(handle="posts", page=params.postPage, perPage=10, order="createdAt DESC");
comments = model("Comment").findAll(handle="comments", page=params.commentPage, perPage=5, order="createdAt DESC");
// View code
<cfoutput>
#paginationNav(handle="posts")#
#paginationNav(handle="comments")#
</cfoutput>
//--------------------------------------------------------------------
// Example 5: Show pagination even when there is only one page of results
// View code
<cfoutput>
#paginationNav(showSinglePage=true)#
</cfoutput>
Builds and returns a string containing a password field form control based on the supplied objectName and property.
Note: Pass any additional arguments like class, rel, and id, and the generated tag will also include those values as HTML attributes.
Name
Type
Required
Default
Description
objectName
any
Yes
The variable name of the object to build the form control for.
property
string
Yes
The name of the property to use in the form control.
association
string
No
The name of the association that the property is located on. Used for building nested forms that work with nested properties. If you are building a form with deep nesting, simply pass in a list to the nested object, and Wheels will figure it out.
position
string
No
The position used when referencing a hasMany relationship in the association argument. Used for building nested forms that work with nested properties. If you are building a form with deep nestings, simply pass in a list of positions, and Wheels will figure it out.
label
string
No
useDefaultLabel
The label text to use in the form control.
labelPlacement
string
No
around
Whether to place the label before, after, or wrapped around the form control. Label text placement can be controlled using aroundLeft or aroundRight.
prepend
string
No
String to prepend to the form control. Useful to wrap the form control with HTML tags.
append
string
No
String to append to the form control. Useful to wrap the form control with HTML tags.
prependToLabel
string
No
String to prepend to the form control's label. Useful to wrap the form control with HTML tags.
appendToLabel
string
No
String to append to the form control's label. Useful to wrap the form control with HTML tags.
errorElement
string
No
span
HTML tag to wrap the form control with when the object contains errors.
errorClass
string
No
field-with-errors
The class name of the HTML tag that wraps the form control when there are errors.
encode
any
No
true
Use this argument to decide whether the output of the function should be encoded in order to prevent Cross Site Scripting (XSS) attacks. Set it to true to encode all relevant output for the specific HTML element in question (e.g. tag content, attribute values, and URLs). For HTML elements that have both tag content and attribute values you can set this argument to attributes to only encode attribute values and not tag content.
// 1. Basic password field bound to a `user` object
<cfoutput>
#passwordField(objectName="user", property="password", label="Password")#
</cfoutput>
// -> <label for="user-password">Password<input id="user-password" type="password" name="user[password]" value=""></label>
// 2. Password field with a confirmation property on the same object
<cfoutput>
#passwordField(objectName="user", property="password", label="Password")#
#passwordField(objectName="user", property="passwordConfirmation", label="Confirm Password")#
</cfoutput>
// 3. Password fields for nested `passwords` association (hasMany)
<fieldset>
<legend>Passwords</legend>
<cfloop from="1" to="#ArrayLen(user.passwords)#" index="i">
#passwordField(objectName="user", association="passwords", position=i, property="password", label="Password ##i#")#
</cfloop>
</fieldset>
// 4. Wrap the field with custom HTML using `prepend` and `append`
<cfoutput>
#passwordField(objectName="user", property="password", label="Password", prepend="<div class=""field"">", append="</div>")#
</cfoutput>
Builds and returns a string containing a password field form control based on the supplied name.
Note: Pass any additional arguments like class, rel, and id, and the generated tag will also include those values as HTML attributes.
Name
Type
Required
Default
Description
name
string
Yes
Name to populate in tag's name attribute.
value
string
No
Value to populate in tag's value attribute.
label
string
No
The label text to use in the form control.
labelPlacement
string
No
around
Whether to place the label before, after, or wrapped around the form control. Label text placement can be controlled using aroundLeft or aroundRight.
prepend
string
No
String to prepend to the form control. Useful to wrap the form control with HTML tags.
append
string
No
String to append to the form control. Useful to wrap the form control with HTML tags.
prependToLabel
string
No
String to prepend to the form control's label. Useful to wrap the form control with HTML tags.
appendToLabel
string
No
String to append to the form control's label. Useful to wrap the form control with HTML tags.
encode
any
No
true
Use this argument to decide whether the output of the function should be encoded in order to prevent Cross Site Scripting (XSS) attacks. Set it to true to encode all relevant output for the specific HTML element in question (e.g. tag content, attribute values, and URLs). For HTML elements that have both tag content and attribute values you can set this argument to attributes to only encode attribute values and not tag content.
// 1. Basic password field with just a name
<cfoutput>
#passwordFieldTag(name="password")#
</cfoutput>
// -> <input type="password" name="password" id="password" value="">
// 2. With a label and a pre-filled value (e.g. re-displaying on validation error)
<cfoutput>
#passwordFieldTag(name="password", label="Password", value=params.password)#
</cfoutput>
// -> <label for="password">Password<input type="password" name="password" id="password" value=""></label>
// 3. Label placed before the field, with HTML wrappers using prepend/append
<cfoutput>
#passwordFieldTag(name="password", label="Password", labelPlacement="before", prepend="<div class=""field"">", append="</div>")#
</cfoutput>
// -> <div class="field"><label for="password">Password</label><input type="password" name="password" id="password" value=""></div>
Create a route that matches a URL requiring an HTTP PATCH method. We recommend using this matcher to expose actions that update database records.
Name
Type
Required
Default
Description
name
string
No
Camel-case name of route to reference when build links and form actions (e.g., blogPost).
pattern
string
No
Overrides the URL pattern that will match the route. The default value is a dasherized version of name (e.g., a name of blogPost generates a pattern of blog-post).
to
string
No
Set controller##action combination to map the route to. You may use either this argument or a combination of controller and action.
controller
string
No
Map the route to a given controller. This must be passed along with the action argument.
action
string
No
Map the route to a given action within the controller. This must be passed along with the controller argument.
package
string
No
Indicates a subfolder that the controller will be referenced from (but not added to the URL pattern). For example, if you set this to admin, the controller will be located at admin/YourController.cfc, but the URL path will not contain admin/.
on
string
No
If this route is within a nested resource, you can set this argument to member or collection. A member route contains a reference to the resource's key, while a collection route does not.
redirect
string
No
Redirect via 302 to this URL when this route is matched. Has precedence over controller/action. Use either an absolute link like /about/, or a full canonical link.
Returns the plural form of the passed in word. Can also pluralize a word based on a value passed to the count argument. Wheels stores a list of words that are the same in both singular and plural form (e.g. "equipment", "information") and words that don't follow the regular pluralization rules (e.g. "child" / "children", "foot" / "feet"). Use get("uncountables") / set("uncountables", newList) and get("irregulars") / set("irregulars", newList) to modify them to suit your needs.
Name
Type
Required
Default
Description
word
string
Yes
The word to pluralize.
count
numeric
No
-1
Pluralization will occur when this value is not 1.
returnCount
boolean
No
true
Will return count prepended to the pluralization when true and count is not -1.
// 1. Pluralize a word using standard rules
writeOutput(pluralize("person"));
// -> "people"
// 2. Pluralize based on a count; returns count prepended to the word
writeOutput(pluralize(word="comment", count=1));
// -> "1 comment"
writeOutput(pluralize(word="comment", count=5));
// -> "5 comments"
// 3. Pluralize based on a count but omit the count from the output
writeOutput(pluralize(word="person", count=users.RecordCount, returnCount=false));
// -> "people" (when RecordCount != 1) or "person" (when RecordCount == 1)
Narrows a collection to the records the current user may see by delegating
to the policy's scope() method. Returns whatever the policy returns —
conventionally a chainable finder you keep composing:
function index() {
posts = policyScope(model("Post")).findAll(page = params.page, perPage = 25);
}
Pass the model class first and chain scopes after the call
(policyScope(model("Post")).active()) — a query-builder or scope chain
that is already in flight cannot be introspected for its model. When the
policy class is missing, this throws Wheels.Policy.NotDefined in
development/testing and returns a default-deny (no rows) chain in
production.
Create a route that matches a URL requiring an HTTP POST method. We recommend using this matcher to expose actions that create database records.
Name
Type
Required
Default
Description
name
string
No
Camel-case name of route to reference when build links and form actions (e.g., blogPosts).
pattern
string
No
Overrides the URL pattern that will match the route. The default value is a dasherized version of name (e.g., a name of blogPosts generates a pattern of blog-posts).
to
string
No
Set controller##action combination to map the route to. You may use either this argument or a combination of controller and action.
controller
string
No
Map the route to a given controller. This must be passed along with the action argument.
action
string
No
Map the route to a given action within the controller. This must be passed along with the controller argument.
package
string
No
Indicates a subfolder that the controller will be referenced from (but not added to the URL pattern). For example, if you set this to admin, the controller will be located at admin/YourController.cfc, but the URL path will not contain admin/.
on
string
No
If this route is within a nested resource, you can set this argument to member or collection. A member route contains a reference to the resource's key, while a collection route does not.
redirect
string
No
Redirect via 302 to this URL when this route is matched. Has precedence over controller/action. Use either an absolute link like /about/, or a full canonical link.
<cfscript>
mapper()
// 1. Basic POST route using `to` shorthand (controller##action)
// Route name: widgets
// Example URL: /sites/918/widgets
// Controller: Widgets
// Action: create
.post(name="widgets", pattern="sites/[siteKey]/widgets", to="widgets##create")
// 2. POST route using explicit `controller` and `action` arguments
// Route name: wadgets
// Example URL: /wadgets
// Controller: Wadgets
// Action: create
.post(name="wadgets", controller="wadgets", action="create")
// 3. POST route with a custom URL pattern (e.g., format-bearing endpoint)
// Route name: authenticate
// Example URL: /oauth/token.json
// Controller: Tokens
// Action: create
.post(name="authenticate", pattern="oauth/token.json", to="tokens##create")
// 4. POST route scoped to a package (subfolder) — package not in URL
// Route name: usersPreferences
// Example URL: /preferences
// Controller: users.Preferences
// Action: create
.post(name="preferences", to="preferences##create", package="users")
// 5. POST route with both a custom pattern and a package
// Route name: extranetOrders
// Example URL: /buy-now/orders
// Controller: extranet.Orders
// Action: create
.post(
name="orders",
pattern="buy-now/orders",
to="orders##create",
package="extranet"
)
// 6. POST route that issues a 302 redirect instead of dispatching to a controller
// Route name: legacySignup
// Example URL: /signup → redirects to /register
.post(name="legacySignup", pattern="signup", redirect="/register")
// 7. POST routes nested inside a `resources` block using `on`
.resources(name="customers", nested=true)
// Route name: leadsCustomers
// Example URL: /customers/leads
// Controller: Leads
// Action: create
.post(name="leads", to="leads##create", on="collection")
// Route name: cancelCustomer
// Example URL: /customers/3209/cancel
// Controller: Cancellations
// Action: create
.post(name="cancel", to="cancellations##create", on="member")
.end()
.end();
</cfscript>
Records a version as applied in wheels_migrator_versions without
running its up() method. Useful when a peer applied the migration
via direct SQL or a different tool and you need the tracking
table to reflect that. Refuses if the version is already applied,
or if no local file matches (only known versions can be pretended).
Returns: {success, recorded, message}
Name
Type
Required
Default
Description
version
string
Yes
The version string to record (digits only after sanitisation).
Creates a link to the previous page, or a disabled span when on the first page.
Name
Type
Required
Default
Description
text
string
No
Previous
The text for the link.
handle
string
No
query
The handle given to the query that the pagination should be displayed for.
name
string
No
page
The name of the param that holds the current page number.
class
string
No
CSS class for the link element.
disabledClass
string
No
disabled
CSS class for the disabled span element.
showDisabled
boolean
No
true
Whether to render a disabled span when on the first page.
pageNumberAsParam
boolean
No
true
Decides whether to link the page number as a param or as part of a route.
encode
any
No
true
Use this argument to decide whether the output of the function should be encoded in order to prevent Cross Site Scripting (XSS) attacks. Set it to true to encode all relevant output for the specific HTML element in question (e.g. tag content, attribute values, and URLs). For HTML elements that have both tag content and attribute values you can set this argument to attributes to only encode attribute values and not tag content.
//--------------------------------------------------------------------
// Example 1: Basic usage — show a "Previous" link above a paginated list;
// renders a disabled span when already on the first page
// Controller code
param name="params.page" type="integer" default="1";
posts = model("Post").findAll(page=params.page, perPage=10, order="createdAt DESC");
// View code
<cfoutput>
#firstPageLink()#
#previousPageLink()#
#pageNumberLinks()#
#nextPageLink()#
#lastPageLink()#
</cfoutput>
//--------------------------------------------------------------------
// Example 2: Custom link text and CSS classes
// View code
<cfoutput>
#previousPageLink(
text="« Previous",
class="page-link",
disabledClass="page-link disabled"
)#
</cfoutput>
//--------------------------------------------------------------------
// Example 3: Hide the disabled element entirely when on the first page
// View code
<cfoutput>
#previousPageLink(showDisabled=false)#
</cfoutput>
//--------------------------------------------------------------------
// Example 4: Use a named route so page numbers appear in the URL path
// instead of as a query-string param (e.g. /articles/page/2)
// Route setup in app/config/routes.cfm
mapper()
.get(name="paginatedArticles", pattern="articles/page/[page]", to="articles##index")
.get(name="articles", pattern="articles", to="articles##index")
.end();
// Controller code
param name="params.page" type="integer" default="1";
articles = model("Article").findAll(page=params.page, perPage=20, order="title");
// View code
<cfoutput>
#previousPageLink(route="paginatedArticles", pageNumberAsParam=false)#
</cfoutput>
Returns the name of the primary key for this model's table.
This is determined through database introspection.
If composite primary keys have been used, they will both be returned in a list.
This function is also aliased as primaryKeys().
Name
Type
Required
Default
Description
position
numeric
No
0
If you are accessing a composite primary key, pass the position of a single key to fetch.
// 1. Get the name of the primary key for the `employee` model (maps to the `employees` table by default)
keyName = model("employee").primaryKey();
// keyName -> "id"
// 2. Get all primary key column names for a model with a composite primary key
keys = model("orderItem").primaryKey();
// keys -> "orderId,productId"
// 3. Get only the first key of a composite primary key using the `position` argument
firstKey = model("orderItem").primaryKey(1);
// firstKey -> "orderId"
// 4. Use the `primaryKeys()` alias (preferred for readability with composite keys)
keys = model("orderItem").primaryKeys();
// keys -> "orderId,productId"
Adds a primary key definition to the table. this method also allows for multiple primary keys.
Accepts columnName / columnNames as aliases for name (per #2803) so the
PK helper matches the argument-naming convention every other column helper
in this file uses. The legacy name parameter keeps working — it is still
what the body reads and what init() passes when adding the conventional
id primary key.
Name
Type
Required
Default
Description
name
string
No
Legacy parameter for the primary-key column name. New code should prefer columnName.
columnName
string
No
Modern singular alias for name (matches sibling column helpers).
columnNames
string
No
Modern plural alias for name. Accepted for muscle-memory parity with t.integer(columnNames=...) etc. NOTE: unlike sibling helpers, this does NOT accept a comma-separated list — primaryKey() always creates one PK column, so columnNames="a,b" produces a single column literally named a,b (not two PKs). For composite PKs call t.primaryKey() multiple times.
Alias for primaryKey().
Use this for better readability when you're accessing multiple primary keys.
Name
Type
Required
Default
Description
position
numeric
No
0
If you are accessing a composite primary key, pass the position of a single key to fetch.
// 1. Get the name(s) of the primary key(s) for the User model (returns a comma-separated list for composite keys)
keyNames = model("User").primaryKeys();
// keyNames -> "id"
// 2. Get the name of the first primary key in a model that uses a composite primary key (e.g., an OrderItem table keyed on orderId,productId)
firstKey = model("OrderItem").primaryKeys(1);
// firstKey -> "orderId"
// 3. Get the second primary key in a composite key model
secondKey = model("OrderItem").primaryKeys(2);
// secondKey -> "productId"
Process the specified action of the controller.
This is exposed in the API primarily for testing purposes; you would not usually call it directly unless in the test suite.
Name
Type
Required
Default
Description
includeFilters
string
No
true
Set to before to only execute "before" filters, after to only execute "after" filters or false to skip all filters. This argument is generally inherited from the processRequest function during unit test execution.
// 1. Process the current action (runs before and after filters plus the action itself).
// Typically called automatically by the framework; used directly in unit tests.
result = controller.processAction();
// result -> true
// 2. Process the action running only "before" filters (skip "after" filters).
result = controller.processAction(includeFilters="before");
// result -> true
// 3. Process the action without running any filters.
result = controller.processAction(includeFilters=false);
// result -> true
Creates a controller and calls an action on it.
Which controller and action that's called is determined by the params passed in.
Returns the result of the request either as a string or in a struct with body, emails, files, flash, redirect, status, and type.
Primarily used for testing purposes.
Name
Type
Required
Default
Description
params
struct
Yes
The params struct to use in the request (make sure that at least controller and action are set).
method
string
No
get
The HTTP method to use in the request (get, post etc).
returnAs
string
No
Pass in struct to return all information about the request instead of just the final output (body).
rollback
string
No
false
Pass in true to roll back all database transactions made during the request.
includeFilters
string
No
true
Set to before to only execute "before" filters, after to only execute "after" filters or false to skip all filters.
// 1. Basic usage: simulate a GET request to the Users#index action and return the rendered body.
result = processRequest(params={controller="users", action="index"});
// result -> "<html>...</html>"
// 2. Simulate a POST request to create a new user and return the full response struct.
result = processRequest(
params={controller="users", action="create", firstName="Jane", lastName="Doe"},
method="post",
returnAs="struct"
);
// result.status -> 302
// result.redirect -> "/users"
// result.body -> ""
// result.flash -> {success="User created."}
// 3. Roll back all database changes made during the request (useful in tests to keep data clean).
result = processRequest(
params={controller="users", action="create", firstName="Jane"},
method="post",
rollback=true,
returnAs="struct"
);
// result.status -> 302
// 4. Run the action without any filters (bypass before/after filter logic).
result = processRequest(
params={controller="users", action="index"},
includeFilters=false
);
// result -> "<html>...</html>"
Returns a structure of all the properties with their names as keys and the values of the property as values.
Name
Type
Required
Default
Description
returnIncluded
boolean
No
true
Whether to return nested properties or not.
// 1. Get all properties of a model object as a struct
user = model("User").findByKey(1);
props = user.properties();
// props -> {id: 1, firstName: "Jane", lastName: "Doe", email: "jane@example.com", createdAt: ...}
// 2. Exclude nested (included) association properties from the result
// Useful when you only want the object's own scalar properties
post = model("Post").findOne(include="comments");
ownProps = post.properties(returnIncluded=false);
// ownProps -> {id: 42, title: "Hello World", body: "...", createdAt: ...}
// (nested `comments` array is omitted)
// 3. Use properties() to pass a model's data as a plain struct (e.g. to a service layer)
user = model("User").findByKey(session.userId);
userService.syncUser(user.properties());
Use this method to map an object property to either a table column with a different name than the property or to a SQL expression.
You only need to use this method when you want to override the default object relational mapping that Wheels performs.
Name
Type
Required
Default
Description
name
string
Yes
The name that you want to use for the column or SQL function result in the CFML code.
column
string
No
The name of the column in the database table to map the property to.
sql
string
No
An SQL expression to use to calculate the property value.
label
string
No
A custom label for this property to be referenced in the interface and error messages.
defaultValue
string
No
A default value for this property.
select
boolean
No
true
Whether to include this property by default in SELECT statements
dataType
string
No
char
Specify the column dataType for this property
automaticValidations
boolean
No
Enable / disable automatic validations for this property.
// 1. Map a CFML property name to a differently-named database column
// Tell Wheels that `firstName` in CFML maps to `STR_USERS_FNAME` in the database
// instead of the default `firstname` column
property(name="firstName", column="STR_USERS_FNAME");
// 2. Create a calculated property using a SQL expression
// `fullName` is derived by concatenating two columns at the database level
property(name="fullName", sql="STR_USERS_FNAME + ' ' + STR_USERS_LNAME");
// 3. Set a custom label used in form helpers and validation error messages
property(name="firstName", label="First name(s)");
// 4. Specify a default value applied when creating new objects
property(name="firstName", defaultValue="Dave");
// 5. Define a calculated property with a specific data type and exclude it from default SELECTs
// Useful when the SQL expression returns a numeric result or when you only need
// the value in specific queries
property(name="orderTotal", sql="SUM(line_items.price)", dataType="decimal", select=false);
// 6. Disable automatic validations for a specific property
// Wheels normally infers validations (e.g. string-length, numeric) from the column type;
// set automaticValidations=false to skip that for this property
property(name="legacyCode", automaticValidations=false);
Returns true if the specified property doesn't exist on the model or is an empty string.
This method is the inverse of propertyIsPresent().
Name
Type
Required
Default
Description
property
string
Yes
Name of property to inspect.
// 1. Check if a property is blank before sending a notification
user = model("User").findByKey(params.userId);
if (user.propertyIsBlank("email")) {
flashInsert(error="Please provide an email address before continuing.");
}
// 2. Conditionally set a default value when a property is blank
product = model("Product").findByKey(params.id);
if (product.propertyIsBlank("description")) {
product.description = "No description available.";
product.save();
}
// 3. Use propertyIsBlank alongside its inverse propertyIsPresent for branching logic
post = model("Post").findByKey(params.postId);
if (post.propertyIsBlank("publishedAt")) {
// post has never been published
writeOutput("Draft");
} else {
// propertyIsPresent("publishedAt") would return true here
writeOutput("Published on #post.publishedAt#");
}
Returns true if the specified property exists on the model and is not a blank string.
Name
Type
Required
Default
Description
property
string
Yes
Name of property to inspect.
// 1. Check if a non-blank property is present
employee = model("Employee").new();
employee.firstName = "Jane";
writeOutput(employee.propertyIsPresent("firstName")); // true
// 2. Returns false when the property is an empty string
employee = model("Employee").new();
employee.firstName = "";
writeOutput(employee.propertyIsPresent("firstName")); // false
// 3. Returns false when the property does not exist on the object
employee = model("Employee").new();
writeOutput(employee.propertyIsPresent("nonExistentField")); // false
Returns a list of property names ordered by their respective column's ordinal position in the database table.
Also includes calculated property names that will be generated by the Wheels ORM.
// 1. Get a comma-delimited list of all property names for the User model
propNames = model("User").propertyNames();
// propNames -> "id,firstName,lastName,email,createdAt,updatedAt"
// 2. Check whether a specific property exists on the model before accessing it
propNames = model("User").propertyNames();
if (listFindNoCase(propNames, "email")) {
writeOutput("email is a valid property");
}
// 3. Property names include calculated properties defined with property(sql="...")
// In User.cfc config():
// property(name="fullName", sql="firstName || ' ' || lastName");
propNames = model("User").propertyNames();
// propNames -> "id,firstName,lastName,email,createdAt,updatedAt,fullName"
Use this method to specify which properties cannot be set through mass assignment.
Name
Type
Required
Default
Description
properties
string
No
Property name (or list of property names) that are not allowed to be altered through mass assignment.
// 1. Protect a comma-delimited list of properties from mass assignment in `models/User.cfc`.
// `firstName` and `lastName` cannot be changed via `updateAll()`, `new()`, `update()`, etc.
function config() {
protectedProperties("firstName,lastName");
}
// 2. Using the named argument form to protect sensitive fields like `role` and `isAdmin`
function config() {
protectedProperties(properties="role,isAdmin");
}
Tells Wheels to protect POSTed requests from CSRF vulnerabilities.
Instructs the controller to verify that params.authenticityToken or X-CSRF-Token HTTP header is provided along with the request containing a valid authenticity token.
Call this method within a controller's config method, preferably the base Controller.cfc file, to protect the entire application.
Name
Type
Required
Default
Description
with
string
No
exception
How to handle invalid authenticity token checks. Valid values are exception (the default — throws a Wheels.InvalidAuthenticityToken error), abort (aborts the request silently and sends a blank response to the client), and ignore (ignores the check and lets the request proceed).
only
string
No
List of actions that this check should only run on. Leave blank for all.
except
string
No
List of actions that this check should be omitted from running on. Leave blank for no exceptions.
// 1. Protect all POST actions across the entire application (add to the base Controller.cfc).
component extends="Controller" {
function config() {
protectsFromForgery();
}
}
// 2. Abort silently on an invalid token instead of throwing an exception.
component extends="Controller" {
function config() {
protectsFromForgery(with="abort");
}
}
// 3. Enable CSRF protection only on state-changing actions.
component extends="Controller" {
function config() {
protectsFromForgery(only="create, update, delete");
}
}
// 4. Enable CSRF protection globally but skip it for a public API endpoint.
component extends="Controller" {
function config() {
protectsFromForgery(except="apiReceive");
}
}
Defines formats that the controller will respond with upon request.
The format can be requested through a URL variable called format, by appending the format name to the end of a URL as an extension (when URL rewriting is enabled), or in the request header.
Name
Type
Required
Default
Description
formats
string
No
Formats to instruct the controller to provide. Valid values are html (the default), xml, json, csv, pdf, and xls.
// 1. Allow the controller to respond to HTML and JSON requests
// Place this call inside the controller's config() function
provides("html,json");
// 2. Allow all supported formats for an API controller
provides("html,xml,json,csv,pdf,xls");
// 3. JSON-only controller (e.g. a pure REST API controller)
// Any request for a format other than json will be rejected
provides("json");
Publish an event to a channel.
Delegates to the in-memory Channel engine or the DatabaseAdapter
depending on the adapter argument (or the global channelAdapter setting).
Can be called from controllers, models, jobs, or anywhere with access
to global helpers.
Name
Type
Required
Default
Description
channel
string
Yes
The channel name to publish to (e.g. "user.42").
event
string
Yes
The event type (e.g. "notification", "update").
data
string
Yes
The event data as a string (typically JSON).
adapter
string
No
Adapter to use: "memory" (default) or "database".
// 1. Publish a notification event to a user-specific channel (in-memory adapter)
data = serializeJSON({message = "Your order has shipped!", orderId = 42});
result = publish(channel="user.42", event="notification", data=data);
// result -> {success: true, ...}
// 2. Publish an update event using the database adapter for persistence
data = serializeJSON({status = "active", updatedAt = Now()});
result = publish(channel="products", event="update", data=data, adapter="database");
// 3. Broadcast a chat message to a room channel
result = publish(
channel = "chat.room.5",
event = "message",
data = serializeJSON({user = "alice", text = "Hello everyone!"})
);
Create a route that matches a URL requiring an HTTP PUT method. We recommend using this matcher to expose actions that update database records. This method is provided as a convenience for when you really need to support the PUT verb; consider using the patch matcher instead of this one.
Name
Type
Required
Default
Description
name
string
No
Camel-case name of route to reference when build links and form actions (e.g., blogPost).
pattern
string
No
Overrides the URL pattern that will match the route. The default value is a dasherized version of name (e.g., a name of blogPost generates a pattern of blog-post).
to
string
No
Set controller##action combination to map the route to. You may use either this argument or a combination of controller and action.
controller
string
No
Map the route to a given controller. This must be passed along with the action argument.
action
string
No
Map the route to a given action within the controller. This must be passed along with the controller argument.
package
string
No
Indicates a subfolder that the controller will be referenced from (but not added to the URL pattern). For example, if you set this to admin, the controller will be located at admin/YourController.cfc, but the URL path will not contain admin/.
on
string
No
If this route is within a nested resource, you can set this argument to member or collection. A member route contains a reference to the resource's key, while a collection route does not.
redirect
string
No
Redirect via 302 to this URL when this route is matched. Has precedence over controller/action. Use either an absolute link like /about/, or a full canonical link.
<cfscript>
mapper()
// 1. Basic PUT route using "to" shorthand (controller##action)
// Route name: ghostStory
// Example URL: /ghosts/666/stories/616
// Controller: Stories
// Action: update
.put(name="ghostStory", pattern="ghosts/[ghostKey]/stories/[key]", to="stories##update")
// 2. Explicit controller and action arguments
// Route name: goblins
// Example URL: /goblins
// Controller: Goblins
// Action: update
.put(name="goblins", controller="goblins", action="update")
// 3. Minimal "to" — pattern derived from name
// Route name: heartbeat
// Example URL: /heartbeat
// Controller: Sessions
// Action: update
.put(name="heartbeat", to="sessions##update")
// 4. Scoped to a package (subfolder) — package not added to URL
// Route name: usersPreferences
// Example URL: /preferences
// Controller: users.Preferences
// Action: update
.put(name="preferences", to="preferences##update", package="users")
// 5. Package combined with an explicit pattern
// Route name: orderShipment
// Example URL: /shipments/5432
// Controller: orders.Shipments
// Action: update
.put(
name="shipment",
pattern="shipments/[key]",
to="shipments##update",
package="orders"
)
// 6. Permanent redirect — useful when an endpoint has moved
// Example URL: /legacy-profile -> redirects to /profile
.put(name="legacyProfile", pattern="legacy-profile", redirect="/profile")
// 7. "on" argument within a nested resource
.resources(name="subscribers", nested=true)
// Route name: launchSubscribers
// Example URL: /subscribers/launch
// Controller: Subscribers
// Action: launch
.put(name="launch", to="subscribers##launch", on="collection")
// Route name: discontinueSubscriber
// Example URL: /subscribers/2251/discontinue
// Controller: Subscribers
// Action: discontinue
.put(name="discontinue", to="subscribers##discontinue", on="member")
.end()
.end();
</cfscript>
Builds and returns a string containing a radio button form control based on the supplied objectName and property.
Note: Pass any additional arguments like class, rel, and id, and the generated tag will also include those values as HTML attributes.
Name
Type
Required
Default
Description
objectName
any
Yes
The variable name of the object to build the form control for.
property
string
Yes
The name of the property to use in the form control.
association
string
No
The name of the association that the property is located on. Used for building nested forms that work with nested properties. If you are building a form with deep nesting, simply pass in a list to the nested object, and Wheels will figure it out.
position
string
No
The position used when referencing a hasMany relationship in the association argument. Used for building nested forms that work with nested properties. If you are building a form with deep nestings, simply pass in a list of positions, and Wheels will figure it out.
tagValue
string
No
The value of the radio button when selected.
label
string
No
useDefaultLabel
The label text to use in the form control.
labelPlacement
string
No
around
Whether to place the label before, after, or wrapped around the form control. Label text placement can be controlled using aroundLeft or aroundRight.
prepend
string
No
String to prepend to the form control. Useful to wrap the form control with HTML tags.
append
string
No
String to append to the form control. Useful to wrap the form control with HTML tags.
prependToLabel
string
No
String to prepend to the form control's label. Useful to wrap the form control with HTML tags.
appendToLabel
string
No
String to append to the form control's label. Useful to wrap the form control with HTML tags.
errorElement
string
No
span
HTML tag to wrap the form control with when the object contains errors.
errorClass
string
No
field-with-errors
The class name of the HTML tag that wraps the form control when there are errors.
encode
any
No
true
Use this argument to decide whether the output of the function should be encoded in order to prevent Cross Site Scripting (XSS) attacks. Set it to true to encode all relevant output for the specific HTML element in question (e.g. tag content, attribute values, and URLs). For HTML elements that have both tag content and attribute values you can set this argument to attributes to only encode attribute values and not tag content.
// 1. Basic radio buttons for a gender property on a user object
<cfoutput>
<fieldset>
<legend>Gender</legend>
#radioButton(objectName="user", property="gender", tagValue="m", label="Male")#
#radioButton(objectName="user", property="gender", tagValue="f", label="Female")#
</fieldset>
</cfoutput>
// 2. Radio buttons with label placed after the control and a CSS class applied
<cfoutput>
#radioButton(objectName="user", property="status", tagValue="active", label="Active", labelPlacement="after", class="status-radio")#
#radioButton(objectName="user", property="status", tagValue="inactive", label="Inactive", labelPlacement="after", class="status-radio")#
</cfoutput>
// 3. Radio buttons for a nested hasMany association (e.g. setting each committee member's gender)
<cfoutput>
<cfloop from="1" to="#ArrayLen(committee.members)#" index="i">
<div>
<h3>#committee.members[i].fullName#:</h3>
#radioButton(objectName="committee", association="members", position=i, property="gender", tagValue="m", label="Male")#
#radioButton(objectName="committee", association="members", position=i, property="gender", tagValue="f", label="Female")#
</div>
</cfloop>
</cfoutput>
Builds and returns a string containing a radio button form control based on the supplied name.
Note: Pass any additional arguments like class, rel, and id, and the generated tag will also include those values as HTML attributes.
Name
Type
Required
Default
Description
name
string
Yes
Name to populate in tag's name attribute.
value
string
Yes
Value to populate in tag's value attribute.
checked
boolean
No
false
Whether or not to check the radio button by default.
label
string
No
The label text to use in the form control.
labelPlacement
string
No
around
Whether to place the label before, after, or wrapped around the form control. Label text placement can be controlled using aroundLeft or aroundRight.
prepend
string
No
String to prepend to the form control. Useful to wrap the form control with HTML tags.
append
string
No
String to append to the form control. Useful to wrap the form control with HTML tags.
prependToLabel
string
No
String to prepend to the form control's label. Useful to wrap the form control with HTML tags.
appendToLabel
string
No
String to append to the form control's label. Useful to wrap the form control with HTML tags.
encode
any
No
true
Use this argument to decide whether the output of the function should be encoded in order to prevent Cross Site Scripting (XSS) attacks. Set it to true to encode all relevant output for the specific HTML element in question (e.g. tag content, attribute values, and URLs). For HTML elements that have both tag content and attribute values you can set this argument to attributes to only encode attribute values and not tag content.
// 1. Basic usage with a group of radio buttons sharing the same name
<cfoutput>
<fieldset>
<legend>Gender</legend>
#radioButtonTag(name="gender", value="m", label="Male", checked=true)#
#radioButtonTag(name="gender", value="f", label="Female")#
</fieldset>
</cfoutput>
// 2. Place the label after the radio button instead of wrapping it
<cfoutput>
#radioButtonTag(name="size", value="s", label="Small", labelPlacement="after")#
#radioButtonTag(name="size", value="m", label="Medium", labelPlacement="after")#
#radioButtonTag(name="size", value="l", label="Large", labelPlacement="after")#
</cfoutput>
// 3. Loop over a query to render one radio button per option
// Controller
sizes = model("Size").findAll(order="position");
// View
<cfoutput query="sizes">
#radioButtonTag(
name = "sizeId",
value = sizes.id,
label = sizes.name,
checked = sizes.id EQ params.sizeId
)#
</cfoutput>
Builds and returns a string containing a range slider form control based on the supplied objectName and property.
Note: Pass any additional arguments like class, rel, and id, and the generated tag will also include those values as HTML attributes.
Name
Type
Required
Default
Description
objectName
any
Yes
The variable name of the object to build the form control for.
property
string
Yes
The name of the property to use in the form control.
association
string
No
The name of the association that the property is located on. Used for building nested forms that work with nested properties. If you are building a form with deep nesting, simply pass in a list to the nested object, and Wheels will figure it out.
position
string
No
The position used when referencing a hasMany relationship in the association argument. Used for building nested forms that work with nested properties. If you are building a form with deep nestings, simply pass in a list of positions, and Wheels will figure it out.
min
string
No
Minimum allowed value.
max
string
No
Maximum allowed value.
step
string
No
Stepping interval.
label
string
No
useDefaultLabel
The label text to use in the form control.
labelPlacement
string
No
around
Whether to place the label before, after, or wrapped around the form control. Label text placement can be controlled using aroundLeft or aroundRight.
prepend
string
No
String to prepend to the form control. Useful to wrap the form control with HTML tags.
append
string
No
String to append to the form control. Useful to wrap the form control with HTML tags.
prependToLabel
string
No
String to prepend to the form control's label. Useful to wrap the form control with HTML tags.
appendToLabel
string
No
String to append to the form control's label. Useful to wrap the form control with HTML tags.
errorElement
string
No
span
HTML tag to wrap the form control with when the object contains errors.
errorClass
string
No
field-with-errors
The class name of the HTML tag that wraps the form control when there are errors.
encode
any
No
true
Use this argument to decide whether the output of the function should be encoded in order to prevent Cross Site Scripting (XSS) attacks. Set it to true to encode all relevant output for the specific HTML element in question (e.g. tag content, attribute values, and URLs). For HTML elements that have both tag content and attribute values you can set this argument to attributes to only encode attribute values and not tag content.
// 1. Basic range slider bound to a model object
#rangeField(objectName="settings", property="volume")#
// 2. Range slider with min, max, and step constraints
#rangeField(label="Volume", objectName="settings", property="volume", min="0", max="100", step="5")#
// 3. Range slider for a nested association (preferences within a user profile)
<cfloop from="1" to="#ArrayLen(user.preferences)#" index="i">
#rangeField(label="Threshold ##i#", objectName="user", association="preferences", position=i, property="threshold", min="0", max="10", step="1")#
</cfloop>
Builds and returns a string containing a range slider form control based on the supplied name.
Note: Pass any additional arguments like class, rel, and id, and the generated tag will also include those values as HTML attributes.
Name
Type
Required
Default
Description
name
string
Yes
Name to populate in tag's name attribute.
value
string
No
Value to populate in tag's value attribute.
min
string
No
Minimum allowed value.
max
string
No
Maximum allowed value.
step
string
No
Stepping interval.
label
string
No
The label text to use in the form control.
labelPlacement
string
No
around
Whether to place the label before, after, or wrapped around the form control. Label text placement can be controlled using aroundLeft or aroundRight.
prepend
string
No
String to prepend to the form control. Useful to wrap the form control with HTML tags.
append
string
No
String to append to the form control. Useful to wrap the form control with HTML tags.
prependToLabel
string
No
String to prepend to the form control's label. Useful to wrap the form control with HTML tags.
appendToLabel
string
No
String to append to the form control's label. Useful to wrap the form control with HTML tags.
encode
any
No
true
Use this argument to decide whether the output of the function should be encoded in order to prevent Cross Site Scripting (XSS) attacks. Set it to true to encode all relevant output for the specific HTML element in question (e.g. tag content, attribute values, and URLs). For HTML elements that have both tag content and attribute values you can set this argument to attributes to only encode attribute values and not tag content.
// 1. Basic range slider with a name and current value
#rangeFieldTag(name="volume", value=params.volume)#
// 2. Range slider with min, max, and step constraints plus a label
#rangeFieldTag(name="brightness", value=params.brightness, label="Brightness", min="0", max="100", step="5")#
// 3. Range slider with a CSS class and appended display hint
#rangeFieldTag(name="opacity", value=params.opacity, label="Opacity", min="0", max="1", step="0.1", class="opacity-slider", append="(0–1)")#
Redirects the browser to the supplied controller/action/key, route or back to the referring page.
Internally, this function uses the URLFor function to build the link and the cflocation tag to perform the redirect.
Name
Type
Required
Default
Description
back
boolean
No
false
Set to true to redirect back to the referring page.
addToken
boolean
No
false
See documentation for your CFML engine's implementation of cflocation.
statusCode
numeric
No
302
See documentation for your CFML engine's implementation of cflocation.
route
string
No
Name of a route that you have configured in config/routes.cfm.
method
string
No
HTTP method constraint used when matching routes.
controller
string
No
Name of the controller to include in the URL.
action
string
No
Name of the action to include in the URL.
key
any
No
Key(s) to include in the URL.
params
string
No
Any additional parameters to be set in the query string (example: wheels=cool&x=y). Please note that Wheels uses the & and = characters to split the parameters and encode them properly for you. However, if you need to pass in & or = as part of the value, then you need to encode them (and only them), example: a=cats%26dogs%3Dtrouble!&b=1.
anchor
string
No
Sets an anchor name to be appended to the path.
onlyPath
boolean
No
true
If true, returns only the relative URL (no protocol, host name or port).
host
string
No
Set this to override the current host.
protocol
string
No
Set this to override the current protocol.
port
numeric
No
0
Set this to override the current port number.
url
string
No
Redirect to an external URL.
delay
boolean
No
false
Set to true to delay the redirection until after the rest of your action code has executed.
encode
boolean
No
true
Encode URL parameters using EncodeForURL(). Please note that this does not make the string safe for placement in HTML attributes, for that you need to wrap the result in EncodeForHtmlAttribute() or use linkTo(), startFormTag() etc instead.
// 1. Redirect to an action after successfully saving a user.
if (user.save()) {
redirectTo(action="saveSuccessful");
}
// 2. Redirect to a different controller and action on a secure server with extra query params.
redirectTo(controller="checkout", action="start", params="type=express", protocol="https");
// 3. Redirect to a named route and pass in a dynamic route variable.
redirectTo(route="profile", screenName="Joe");
// 4. Redirect back to the referring page (e.g. after a cancelled edit).
redirectTo(back=true);
// 5. Redirect to an external URL (requires allowExternalRedirects=true in config).
redirectTo(url="https://example.com/landing");
// 6. Delay the redirect until after the rest of the action has run, and set a flash message.
redirectTo(action="index", delay=true, flashMessage="Record deleted successfully.");
// 7. Use a 301 permanent redirect when moving a page.
redirectTo(controller="posts", action="index", statusCode=301);
Reruns the specified migration version. Whilst you can use this in your application, the recommended usage is via either the CLI or the provided GUI interface
Name
Type
Required
Default
Description
version
string
No
The Database schema version to rerun
// 1. Redo the current (most recent) migration — rolls it back then re-applies it
result = application.wheels.migrator.redoMigration();
// result -> "
// ------- 20240315120000_add_status_to_orders ----------------------
// "
// 2. Redo a specific migration version
result = application.wheels.migrator.redoMigration(version="20240101000000");
// result -> "
// ------- 20240101000000_create_users ------------------------------
// "
// 3. Check for errors after redoing a migration
result = application.wheels.migrator.redoMigration(version="20240315120000");
if (FindNoCase("Error", result)) {
writeOutput("Redo failed: " & result);
} else {
writeOutput("Migration redone successfully.");
}
Adds integer reference columns to the table definition and (unless
foreignKey=false or polymorphic=true) registers a matching foreign-key
constraint. The column suffix depends on the useUnderscoreReferenceColumns
setting: false (framework default) → id; true (default for
apps generated by wheels new) → _id, matching Wheels model
belongsTo defaults. With polymorphic=true, a type / _type
companion column is added and no FK is registered.
Accepts columnNames as an alias for referenceNames (per #2781) — both
are list-shaped (single name or comma-delimited). New code should use
columnNames for consistency with every other column helper here.
Name
Type
Required
Default
Description
referenceNames
string
No
Comma-delimited list of reference base names (e.g. "user,role"). Each produces a _id (or id) column. Legacy parameter — columnNames is the modern alias.
columnNames
string
No
Modern alias for referenceNames. Pass one or the other — not both.
default
any
No
Default value for the generated integer column(s).
allowNull
boolean
No
false
If true, the generated column(s) allow NULL.
polymorphic
boolean
No
false
If true, also creates a type / _type companion column and skips the foreign-key constraint.
foreignKey
boolean
No
true
If true (default), registers a foreign key on the generated column. Ignored when polymorphic=true.
onUpdate
string
No
Foreign-key ON UPDATE clause. Engine-specific values; common: "cascade", "null", "none".
onDelete
string
No
Foreign-key ON DELETE clause. Same value set as onUpdate.
// The generated column suffix depends on the `useUnderscoreReferenceColumns` setting:
// `true` (the default for apps generated by `wheels new`) produces `<name>_id` / `<name>_type`,
// matching Wheels model `belongsTo` defaults; `false` (the framework default for existing apps)
// produces `<name>id` / `<name>type`. The examples below show both outcomes.
// 1. Add a single reference column with a foreign key constraint
// Creates a `user_id` (or `userid`) integer column and a foreign key pointing to the `users` table.
t = createTable(name='posts');
t.string(columnNames='title', limit=255, allowNull=false);
t.references(columnNames='user');
t.timestamps();
t.create();
// 2. Add multiple reference columns at once
// Creates `author_id` and `category_id` (or `authorid` and `categoryid`) integer columns,
// each with a foreign key.
t = createTable(name='articles');
t.string(columnNames='title', limit=255, allowNull=false);
t.references(columnNames='author,category');
t.timestamps();
t.create();
// 3. Add a polymorphic reference (no foreign key, adds a `<name>_type` / `<name>type` string column)
// Creates `commentable_id` (integer) and `commentable_type` (string) columns
// (or `commentableid` / `commentabletype` when `useUnderscoreReferenceColumns` is `false`).
t = createTable(name='comments');
t.text(columnNames='body', allowNull=false);
t.references(columnNames='commentable', polymorphic=true);
t.timestamps();
t.create();
// 4. Add a reference with cascade delete and allow null
// The legacy `referenceNames=` argument is still accepted as an alias for `columnNames=`.
t = createTable(name='attachments');
t.references(columnNames='post', allowNull=true, onDelete='cascade');
t.string(columnNames='fileName', limit=255);
t.timestamps();
t.create();
Registers a callback function to be invoked when an unhandled error occurs.
Callbacks receive a single argument: the exception struct.
Multiple callbacks are invoked in registration order. A failing callback
is logged and skipped — it will not prevent other callbacks from running.
Should be called during app initialization, not per-request.
Name
Type
Required
Default
Description
callback
function
Yes
A function that accepts an exception struct argument. Must complete quickly — long-running callbacks delay error responses.
// 1. Register a simple error-notification callback in config/settings.cfm
registerOnError(function(exception) {
writeLog(
file = "app-errors",
type = "error",
text = "Unhandled error: #exception.message# | Type: #exception.type#"
);
});
// 2. Register multiple callbacks — they fire in registration order
registerOnError(function(exception) {
// Notify an external monitoring service
local.payload = serializeJSON({
message = exception.message,
type = exception.type,
detail = exception.detail
});
// cfhttp call to monitoring endpoint would go here
});
registerOnError(function(exception) {
// Store the last error in the application scope for the admin dashboard
application.lastError = {
message = exception.message,
type = exception.type,
timestamp = now()
};
});
// 3. Guard against slow operations — callbacks must complete quickly
registerOnError(function(exception) {
// Do NOT perform long-running tasks here (database queries, large file I/O).
// A failing callback is caught, logged, and skipped so other callbacks still run.
if (structKeyExists(exception, "message") && len(exception.message)) {
writeLog(file = "wheels", type = "error", text = "App error: #exception.message#");
}
});
Reloads the property values of this object from the database.
// 1. Reload after a call that may have changed values in the database
employee = model("Employee").findByKey(params.key);
employee.someCallThatChangesValuesInTheDatabase();
employee.reload();
// 2. Discard in-memory changes and restore the current database values
post = model("Post").findByKey(params.id);
post.title = "Draft title that we want to discard";
post.reload();
// post.title now reflects the value stored in the database
Removes a column from a database table
Only available in a migration CFC
Name
Type
Required
Default
Description
table
string
Yes
The table containing the column to remove
columnName
string
No
The column name to remove
columnNames
string
No
Modern alias for columnName (matches the plural form every TableDefinition column helper accepts). Pass one or the other — not both.
referenceName
string
No
optional reference name
// 1. Remove a column by specifying its name directly
removeColumn(table="members", columnName="status");
// 2. Remove a reference column using its reference name (removes the <referenceName>id column)
removeColumn(table="posts", referenceName="author");
// Removes the column named "authorid" from the posts table
// 3. Typical use inside a migration's down() method to reverse an addColumn()
function down() {
removeColumn(table="products", columnName="discountPrice");
}
Remove a database index
Only available in a migration CFC
Name
Type
Required
Default
Description
table
string
Yes
The table name to perform the index operation on
indexName
string
Yes
the name of the index to remove
// 1. Remove an index by its name
removeIndex(table="members", indexName="members_username");
// 2. Remove a compound index created on multiple columns
// (index was previously added as "orders_customerid_createdat")
removeIndex(table="orders", indexName="orders_customerid_createdat");
// 3. Typical down() migration reversing an addIndex call
component extends="wheels.Migrator" {
function up() {
addIndex(table="articles", columnNames="slug", unique=true, indexName="articles_slug");
}
function down() {
removeIndex(table="articles", indexName="articles_slug");
}
}
Removes existing records from a table
Only available in a migration CFC
Name
Type
Required
Default
Description
table
string
Yes
The table name to remove the record from
where
string
No
The where clause, i.e id = 123
// 1. Remove all records from a table (use with caution)
removeRecord(table = "sessions");
// 2. Remove a specific record by primary key
removeRecord(table = "roles", where = "id = 1");
// 3. Remove multiple records matching a condition
removeRecord(table = "users", where = "active = 0");
// 4. Use removeRecord in a migration's down() function to reverse an addRecord call
component extends="wheels.migrator.Migration" {
function up() {
addRecord(
table = "settings",
name = "maintenanceMode",
value = "false"
);
}
function down() {
removeRecord(table = "settings", where = "name = 'maintenanceMode'");
}
}
Renames a table column
Only available in a migration CFC
Name
Type
Required
Default
Description
table
string
Yes
The table containing the column to rename
columnName
string
Yes
The column name to rename
newColumnName
string
Yes
The new column name
// 1. Rename a column in the users table
renameColumn(table="users", columnName="userName", newColumnName="username");
// 2. Rename a column as part of a migration's up() and down() methods
component extends="wheels.migrator.Migration" hint="Rename fullName to displayName in profiles" {
function up() {
renameColumn(table="profiles", columnName="fullName", newColumnName="displayName");
}
function down() {
renameColumn(table="profiles", columnName="displayName", newColumnName="fullName");
}
}
F15 Phase 2: rename legacy c_o_r_e_* system tables to wheels_*.
Public API for the wheels migrate rename-system-tables CLI command.
Reads the current schema, generates per-adapter rename SQL, and
(unless dryRun is true) executes it inside a transaction. After
a successful rename, updates application.wheels.{levelsTableName,
migratorTableName} to the new names so the running app picks them
up without a restart.
Result struct:
- success: boolean
- renamed: array of "old -> new" strings (empty if no-op)
- skipped: human message when there's nothing to do
- errors: array of error messages (when success=false)
- sql: array of SQL statements that would run / did run
Refuses to run (returns success=false) when both c_o_r_e_* AND
wheels_* versions of either table coexist — that's a partial-
rename state which warrants manual cleanup, not silent destruction.
Name
Type
Required
Default
Description
dryRun
boolean
No
false
When true, returns the SQL that would run without executing.
// 1. Rename legacy c_o_r_e_* tables to wheels_* in one step
result = application.wheels.migrator.renameSystemTables();
// result.success -> true
// result.renamed -> ["c_o_r_e_levels -> wheels_levels", "c_o_r_e_migrator_versions -> wheels_migrator_versions"]
// result.sql -> ["ALTER TABLE c_o_r_e_migrator_versions RENAME TO wheels_migrator_versions", "ALTER TABLE c_o_r_e_levels RENAME TO wheels_levels"]
// result.skipped -> ""
// result.errors -> []
// 2. Dry-run: preview the SQL without executing any changes
result = application.wheels.migrator.renameSystemTables(dryRun=true);
// result.success -> true
// result.renamed -> [] (nothing executed)
// result.sql -> ["ALTER TABLE c_o_r_e_migrator_versions RENAME TO wheels_migrator_versions", "ALTER TABLE c_o_r_e_levels RENAME TO wheels_levels"]
// 3. Handle all possible outcomes after running the rename
result = application.wheels.migrator.renameSystemTables();
if (!result.success) {
// Partial-rename conflict or execution error
writeOutput("Rename failed: " & arrayToList(result.errors, "; "));
} else if (len(result.skipped)) {
// Tables were already on wheels_* names (or no legacy tables found)
writeOutput(result.skipped);
} else {
writeOutput("Renamed: " & arrayToList(result.renamed, ", "));
}
// 1. Rename a table from its old name to a new name
renameTable(oldName="blogPosts", newName="posts");
// 2. Rename a table as part of a migration up/down pair
component extends="wheels.Migrator" {
function up() {
renameTable(oldName="members", newName="users");
}
function down() {
renameTable(oldName="users", newName="members");
}
}
Instructs the controller to render an empty string when it's finished processing the action.
This is very similar to calling cfabort with the advantage that any after filters you have set on the action will still be run.
Name
Type
Required
Default
Description
status
string
No
[runtime expression]
Force request to return with specific HTTP status code.
// 1. Render a blank response (useful for AJAX fire-and-forget actions)
renderNothing();
// 2. Render a blank response with a specific HTTP status code (e.g., 204 No Content)
renderNothing(status=204);
// 3. Use renderNothing() instead of cfabort so that after-filters still run
// In a controller action:
function markAsRead() {
post = model("Post").findByKey(params.key);
post.update(read=true);
// After-filters (e.g. logging) will still execute, unlike cfabort
renderNothing();
}
Instructs the controller to render a partial when it's finished processing the action.
Name
Type
Required
Default
Description
partial
string
Yes
The name of the partial file to be used. Prefix with a leading slash (/) if you need to build a path from the root views folder. Do not include the partial filename's underscore and file extension.
cache
any
No
Number of minutes to cache the content for.
layout
string
No
The layout to wrap the content in. Prefix with a leading slash (/) if you need to build a path from the root views folder. Pass false to not load a layout at all.
returnAs
string
No
Set to string to return the result instead of automatically sending it to the client.
dataFunction
any
No
true
Name of a controller function to load data from.
status
string
No
[runtime expression]
Force request to return with specific HTTP status code.
// 1. Render the partial `_comment.cfm` located in the current controller's view folder
renderPartial("comment");
// 2. Render the partial at `app/views/shared/_comment.cfm` using an absolute path
renderPartial("/shared/comment");
// 3. Return the rendered partial as a string instead of sending it to the client
commentHtml = renderPartial(partial="comment", returnAs="string");
// 4. Render a shared partial wrapped in a layout and cache it for 5 minutes
renderPartial(partial="/shared/sidebar", layout="/layouts/sidebar", cache=5);
// 5. Render a partial using a named data-loading function to supply variables
// (the controller must have a private function named `comment` returning a struct)
renderPartial(partial="comment", dataFunction="comment");
// 6. Render a partial and force a specific HTTP status code (e.g. for AJAX responses)
renderPartial(partial="/shared/error", status=422);
renderSSE()
void
controller
Render a single SSE event as the controller response.
This sets appropriate headers and formats the response as an SSE event.
The client should use EventSource to connect and will receive this single event.
Name
Type
Required
Default
Description
data
string
Yes
The event data to send (string). Will be sent as-is.
event
string
No
Optional event type name. Client can listen for specific event types.
id
string
No
Optional event ID. Client sends Last-Event-ID header on reconnect.
retry
numeric
No
0
Optional reconnection time in milliseconds. Tells client how long to wait before reconnecting.
Instructs the controller to render specified text when it's finished processing the action.
Name
Type
Required
Default
Description
text
string
No
The text to render.
status
any
No
[runtime expression]
Force request to return with specific HTTP status code.
// 1. Render a simple text response to the client
renderText("Done!");
// 2. Render serialized JSON data to the client
products = model("Product").findAll();
renderText(serializeJSON(products));
// 3. Render a plain-text response with a custom HTTP status code
renderText(text="Not authorized", status=401);
Instructs the controller which view template and layout to render when it's finished processing the action.
Note that when passing values for controller and / or action, this function does not execute the actual action but rather just loads the corresponding view template.
Name
Type
Required
Default
Description
controller
string
No
[runtime expression]
Controller to include the view page for.
action
string
No
[runtime expression]
Action to include the view page for.
template
string
No
A specific template to render. Prefix with a leading slash (/) if you need to build a path from the root views folder.
layout
any
No
The layout to wrap the content in. Prefix with a leading slash (/) if you need to build a path from the root views folder. Pass false to not load a layout at all.
cache
any
No
Number of minutes to cache the content for.
returnAs
string
No
Set to string to return the result instead of automatically sending it to the client.
hideDebugInformation
boolean
No
false
Set to true to hide the debug information at the end of the output. This is useful, for example, when you're testing XML output in an environment where the global setting for showDebugInformation is true.
status
string
No
[runtime expression]
Force request to return with specific HTTP status code.
// 1. Render the view template for a different action within the same controller.
renderView(action="edit");
// 2. Render the view template for a different action within a different controller.
renderView(controller="blog", action="new");
// 3. Render a specific template using an absolute path from the `views` folder.
renderView(template="/blog/new");
// 4. Render without a layout and cache the output for 60 minutes.
renderView(layout=false, cache=60);
// 5. Load a layout from a non-default folder within `views`.
renderView(layout="/layouts/blog");
// 6. Return the rendered output as a string instead of sending it to the client.
myView = renderView(returnAs="string");
// 7. Render with a specific HTTP status code (useful for error pages).
renderView(action="notFound", status=404);
// 8. Render XML output and suppress debug information even when `showDebugInformation` is globally enabled.
renderView(template="/reports/summary", layout=false, hideDebugInformation=true);
Instructs the controller to render the data passed in to the format that is requested.
If the format requested is json or xml, Wheels will transform the data into that format automatically.
For other formats (or to override the automatic formatting), you can also create a view template in this format: nameofaction.xml.cfm, nameofaction.json.cfm, nameofaction.pdf.cfm, etc.
Per-action format restrictions set with onlyProvides() are enforced here (since 4.0.4):
when the requested format is not acceptable for the action, renderWith() falls back to
rendering the html view — even when html itself is not in the onlyProvides() list.
Name
Type
Required
Default
Description
data
any
Yes
Data to format and render.
controller
string
No
[runtime expression]
Controller to include the view page for.
action
string
No
[runtime expression]
Action to include the view page for.
template
string
No
A specific template to render. Prefix with a leading slash (/) if you need to build a path from the root views folder.
layout
any
No
The layout to wrap the content in. Prefix with a leading slash (/) if you need to build a path from the root views folder. Pass false to not load a layout at all.
cache
any
No
Number of minutes to cache the content for.
returnAs
string
No
Set to string to return the result instead of automatically sending it to the client.
hideDebugInformation
boolean
No
false
Set to true to hide the debug information at the end of the output. This is useful, for example, when you're testing XML output in an environment where the global setting for showDebugInformation is true.
status
string
No
[runtime expression]
Force request to return with specific HTTP status code.
// 1. Render a query using the format defined in the controller's `config()` function.
// Wheels automatically serializes to JSON or XML when those formats are requested.
products = model("Product").findAll();
renderWith(products);
// 2. Return a JSON error payload with a specific HTTP status code.
msg = {
"status": "Error",
"message": "Not Authenticated"
};
renderWith(data=msg, status=403);
// 3. Render a struct as JSON and return the result as a string instead of sending it to the client.
payload = {"id": 1, "name": "Alice"};
jsonString = renderWith(data=payload, returnAs="string");
// 4. Render with a custom XML template for the current action
// (looks for a view file named `show.xml.cfm` in the current controller's views folder).
user = model("User").findByKey(params.key);
renderWith(data=user, layout=false, hideDebugInformation=true);
// 5. Render data using a specific template from outside the current controller's views folder.
report = model("Order").findAll(select="id,total,createdAt");
renderWith(data=report, template="/reports/summary", layout=false);
Resets a cycle so that it starts from the first list value the next time it is called.
Name
Type
Required
Default
Description
name
string
No
default
The name of the cycle to reset.
// 1. Reset the default cycle between grouped query sections
<cfoutput query="posts" group="categoryId">
resetCycle();
<cfoutput>
rowClass = cycle(values="even,odd");
writeOutput(rowClass & ": " & posts.title);
</cfoutput>
</cfoutput>
// 2. Reset a named cycle so it starts over for each department group
<cfoutput query="employees" group="departmentId">
resetCycle("position");
<cfoutput>
rank = cycle(values="manager,specialist,intern", name="position");
writeOutput(employees.lastName & " - " & rank);
</cfoutput>
</cfoutput>
// 3. Reset all cycles by name after rendering a section
resetCycle("row");
resetCycle("highlight");
Create a group of routes that exposes actions for manipulating a singular resource. A singular resource exposes URL patterns for the entire CRUD lifecycle of a single entity (show, new, create, edit, update, and delete) without exposing a primary key in the URL. Usually this type of resource represents a singleton entity tied to the session, application, or another resource (perhaps nested within another resource). If you need to generate routes for manipulating a collection of resources with a primary key in the URL, see the resources mapper method.
Name
Type
Required
Default
Description
name
string
Yes
Camel-case name of resource to reference when build links and form actions. This is typically a singular word (e.g., profile).
nested
boolean
No
false
Whether or not additional calls will be nested within this resource.
path
string
No
[runtime expression]
Override URL path representing this resource. Default is a dasherized version of name (e.g., blogPost generates a path of blog-post).
controller
string
No
Override name of the controller used by resource. This defaults to a pluralized version of name.
singular
string
No
Override singularize() result in plural resources.
plural
string
No
Override pluralize() result in singular resource.
only
string
No
Limits the list of RESTful routes to generate. Can include show, new, create, edit, update, and delete.
except
string
No
Excludes RESTful routes to generate, taking priority over the only argument. Can include show, new, create, edit,update, and delete.
shallow
boolean
No
Turn on shallow resources.
shallowPath
string
No
Shallow path prefix.
shallowName
string
No
Shallow name prefix.
constraints
struct
No
Variable patterns to use for matching.
callback
any
No
binding
any
No
mapFormat
boolean
No
[runtime expression]
Whether or not to add an optional .[format] pattern to the end of the generated routes. This is useful for providing formats via URL like json, xml, pdf, etc.
<cfscript>
mapper()
// 1. Minimal singular resource — generates show, new, create, edit, update, delete routes
.resource("checkout")
// 2. Point to a controller at a custom path (app/controllers/sessions/Auth.cfc)
.resource(name="auth", controller="sessions/auth")
// 3. Limit generated routes with the `only` argument
.resource(name="profile", only="show,edit,update")
// 4. Exclude specific routes with the `except` argument
.resource(name="cart", except="new,create")
// 5. Nested singular resource — nest additional routes inside, then close with end()
.resource(name="preferences", nested=true)
.get(name="editPassword", to="passwords##edit")
.patch(name="password", to="passwords##update")
.resources("notifications")
.end()
// 6. Override the URL path (blogPostOptions -> blog-post/options instead of blog-post-options)
.resource(name="blogPostOptions", path="blog-post/options")
.end();
</cfscript>
Create a group of routes that exposes actions for manipulating a collection of resources. A plural resource exposes URL patterns for the entire CRUD lifecycle (index, show, new, create, edit, update, delete), exposing a primary key in the URL for showing, editing, updating, and deleting records. If you need to generate routes for manipulating a singular resource without a primary key, see the resource mapper method.
Name
Type
Required
Default
Description
name
string
Yes
Camel-case name of resource to reference when build links and form actions. This is typically a plural word (e.g., posts).
nested
boolean
No
false
Whether or not additional calls will be nested within this resource.
path
string
No
[runtime expression]
Override URL path representing this resource. Default is a dasherized version of name (e.g., blogPosts generates a path of blog-posts).
controller
string
No
Override name of the controller used by resource. This defaults to the value provided for name.
singular
string
No
Override singularize() result in plural resources.
plural
string
No
Override pluralize() result in singular resource.
only
string
No
Limits the list of RESTful routes to generate. Can include index, show, new, create, edit, update, and delete.
except
string
No
Excludes RESTful routes to generate, taking priority over the only argument. Can include index, show, new, create, edit, update, and delete.
shallow
boolean
No
Turn on shallow resources.
shallowPath
string
No
Shallow path prefix.
shallowName
string
No
Shallow name prefix.
constraints
struct
No
Variable patterns to use for matching.
callback
any
No
binding
any
No
mapFormat
boolean
No
[runtime expression]
Whether or not to add an optional .[format] pattern to the end of the generated routes. This is useful for providing formats via URL like json, xml, pdf, etc.
<cfscript>
mapper()
// 1. Basic CRUD resource — generates index, show, new, create, edit, update, delete routes
.resources("admins")
// 2. Point authors URL to controller at `app/controllers/Users.cfc`
.resources(name="authors", controller="users")
// 3. Limit routes to a specific set with the `only` argument
.resources(name="products", only="index,show,edit,update")
// 4. Exclude specific routes using the `except` argument
.resources(name="orders", except="delete")
// 5. Nested resources — child routes receive parent key in URL (e.g. /stories/1/heroes)
.resources(name="stories", nested=true)
.resources("heroes")
.resources("villains")
.end()
// 6. Override the URL path (e.g. /blog-posts/options instead of /blog-posts-options)
.resources(name="blogPostsOptions", path="blog-posts/options")
// 7. Shallow nesting — member routes (show, edit, update, delete) drop the parent prefix
.resources(name="posts", nested=true, shallow=true)
.resources("comments")
.end()
// 8. Constrain URL parameters to a specific pattern (e.g. numeric IDs only)
.resources(name="photos", constraints={key="[0-9]+"})
// 9. Override the singularized form when auto-detection is wrong
.resources(name="people", singular="person")
.end();
</cfscript>
Returns content that Wheels will send to the client in response to the request.
// 1. Get the current response content (empty string if nothing has been rendered yet)
content = response();
// 2. Use in a controller test to verify rendered output
// (Wheels populates the response after renderView, renderText, or renderPartial runs)
renderText("Hello, world!");
assert("response() eq 'Hello, world!'");
// 3. Inspect and modify the response before it is sent to the client
currentContent = response();
if (FindNoCase("<!-- debug -->", currentContent)) {
setResponse(Replace(currentContent, "<!-- debug -->", "", "all"));
}
Create a route that matches the root of its current context. This mapper can be used for the application's web root (or home page), or it can generate a route for the root of a namespace or other path scoping mapper. The route only responds to the GET verb unless you explicitly pass a method (or methods) argument.
Name
Type
Required
Default
Description
to
string
No
Set controller##action combination to map the route to. You may use either this argument or a combination of controller and action.
mapFormat
boolean
No
Set to true to include the format (e.g. .json) in the route.
<cfscript>
// 1. Map the application's web root (home page) to a specific controller action
mapper()
// Map "/" to the `index` action of the `home` controller
.root(to="home##index")
.end();
// 2. Map the root of a namespace scope using separate controller and action arguments
mapper()
.namespace("admin")
// Map "/admin/" to the `dashboard` action of the `admin` controller
.root(controller="admin", action="dashboard")
.end()
.end();
// 3. Map the root with format matching enabled so ".json" etc. are captured
mapper()
.namespace("api")
// Map "/api/" and "/api/.json" (etc.) to the `apis` controller's `index` action
.root(to="apis##index", mapFormat=true)
.end()
.end();
</cfscript>
Saves the object if it passes validation and callbacks.
Returns true if the object was saved successfully to the database, false if not.
Name
Type
Required
Default
Description
parameterize
any
No
true
Set to true to use cfqueryparam on all columns, or pass in a list of property names to use cfqueryparam on those only.
reload
boolean
No
false
Set to true to force Wheels to query the database even though an identical query for this model may have been run in the same request. (The default in Wheels is to get the second query from the model's request-level cache.)
validate
boolean
No
true
Set to false to skip validations for this operation.
transaction
string
No
[runtime expression]
Set this to commit to update the database, rollback to run all the database queries but not commit them, or none to skip transaction handling altogether.
callbacks
boolean
No
true
Set to false to disable callbacks for this method.
// 1. Save a user object to the database (automatically does INSERT or UPDATE depending on whether the record is new)
user.save();
// 2. Use save() in a conditional to handle success and failure
if (user.save()) {
flashInsert(notice="The user was saved successfully!");
redirectTo(action="edit");
} else {
flashInsert(alert="Please correct the errors below.");
renderView(action="edit");
}
// 3. Save without running validations (useful for administrative operations or data migrations)
user.save(validate=false);
// 4. Save using cfqueryparam only on specific properties (pass a list of property names)
user.save(parameterize="firstName,lastName,email");
// 5. Save and force a database reload of the object afterward (instead of using the request-level cache)
user.save(reload=true);
Defines a named query scope that can be chained onto finders.
Scopes allow you to define reusable query fragments in the model config and compose them together.
Name
Type
Required
Default
Description
name
string
Yes
The name of the scope. This becomes a callable method on the model (e.g. model("User").active()).
where
string
No
A WHERE clause fragment to apply when this scope is used.
order
string
No
An ORDER BY clause fragment to apply when this scope is used.
select
string
No
A SELECT clause override to apply when this scope is used.
include
string
No
Associations to include when this scope is used.
maxRows
numeric
No
0
Maximum number of records to return when this scope is used.
handler
string
No
The name of a method on this model that returns a struct of query arguments. Use for dynamic scopes that accept parameters. The method receives any arguments passed to the scope call.
Set any number of parameters to be inherited by mappers called within this matcher's block. For example, set a package or URL path to be used by all child routes.
Name
Type
Required
Default
Description
name
string
No
Name to prepend to child route names for use when building links, forms, and other URLs.
path
string
No
Path to prefix to all child routes.
package
string
No
Package namespace to append to controllers.
controller
string
No
Controller to use for routes.
shallow
boolean
No
Turn on shallow resources to eliminate routing added before this one.
shallowPath
string
No
Shallow path prefix.
shallowName
string
No
Shallow name prefix.
constraints
struct
No
Variable patterns to use for matching.
middleware
any
No
binding
any
No
callback
any
No
A callback function to define nested routes within this scope. If provided, the scope is automatically closed when the callback completes.
<cfscript>
mapper()
// 1. Scope routes to a specific controller.
// All routes inside will use the `freeForAll` controller.
.scope(controller="freeForAll")
.get(name="bananas", action="bananas")
.root(action="index")
.end()
// 2. Scope routes to a package (subfolder) without affecting the URL.
// All routes' controllers inside will be inside the `public` package/subfolder.
.scope(package="public")
.resource(name="search", only="show,create")
.end()
// 3. Scope routes under a URL path prefix.
// All routes inside will be prepended with a URL path of `phones/`.
.scope(path="phones")
.get(name="newest", to="phones##newest")
.get(name="sortOfNew", to="phones##sortOfNew")
.end()
// 4. Scope routes with both a name prefix and URL path prefix.
// Generates named routes like `adminUsers` and `adminPosts`.
.scope(name="admin", path="admin")
.resources(name="users")
.resources(name="posts")
.end()
// 5. Scope routes with URL variable constraints applied to all children.
.scope(constraints={id="[0-9]+"})
.get(name="userProfile", to="users##profile")
.resources(name="orders")
.end()
.end();
</cfscript>
Returns a struct containing all named scope definitions for this model.
Each key is the scope name, and the value is a struct with query fragment keys like where, order, select, include.
// 1. Inspect all named scopes defined on a model
info = model("Article").scopeInfo();
// info -> {
// active: { where: "status = 'active'" },
// recent: { where: "publishedAt > ?", order: "publishedAt DESC" },
// published: { where: "status = 'published'", order: "publishedAt DESC" }
// }
// 2. Check whether a specific scope is defined before using it
info = model("User").scopeInfo();
if (structKeyExists(info, "admins")) {
admins = model("User").admins().findAll();
}
// 3. List all scope names registered on a model
info = model("Post").scopeInfo();
writeOutput(structKeyList(info));
// -> "featured,archived,byDate"
Builds and returns a string containing a search field form control based on the supplied objectName and property.
Note: Pass any additional arguments like class, rel, and id, and the generated tag will also include those values as HTML attributes.
Name
Type
Required
Default
Description
objectName
any
Yes
The variable name of the object to build the form control for.
property
string
Yes
The name of the property to use in the form control.
association
string
No
The name of the association that the property is located on. Used for building nested forms that work with nested properties. If you are building a form with deep nesting, simply pass in a list to the nested object, and Wheels will figure it out.
position
string
No
The position used when referencing a hasMany relationship in the association argument. Used for building nested forms that work with nested properties. If you are building a form with deep nestings, simply pass in a list of positions, and Wheels will figure it out.
label
string
No
useDefaultLabel
The label text to use in the form control.
labelPlacement
string
No
around
Whether to place the label before, after, or wrapped around the form control. Label text placement can be controlled using aroundLeft or aroundRight.
prepend
string
No
String to prepend to the form control. Useful to wrap the form control with HTML tags.
append
string
No
String to append to the form control. Useful to wrap the form control with HTML tags.
prependToLabel
string
No
String to prepend to the form control's label. Useful to wrap the form control with HTML tags.
appendToLabel
string
No
String to append to the form control's label. Useful to wrap the form control with HTML tags.
errorElement
string
No
span
HTML tag to wrap the form control with when the object contains errors.
errorClass
string
No
field-with-errors
The class name of the HTML tag that wraps the form control when there are errors.
encode
any
No
true
Use this argument to decide whether the output of the function should be encoded in order to prevent Cross Site Scripting (XSS) attacks. Set it to true to encode all relevant output for the specific HTML element in question (e.g. tag content, attribute values, and URLs). For HTML elements that have both tag content and attribute values you can set this argument to attributes to only encode attribute values and not tag content.
// 1. Basic search field bound to a model object
#searchField(objectName="searchForm", property="query")#
// 2. Search field with a custom label and placeholder attribute
#searchField(objectName="searchForm", property="query", label="Search", placeholder="Enter keywords...")#
// 3. Search field with label placement after the input and a CSS class
#searchField(objectName="searchForm", property="query", label="Search", labelPlacement="after", class="search-input")#
Builds and returns a string containing a search field form control based on the supplied name.
Note: Pass any additional arguments like class, rel, and id, and the generated tag will also include those values as HTML attributes.
Name
Type
Required
Default
Description
name
string
Yes
Name to populate in tag's name attribute.
value
string
No
Value to populate in tag's value attribute.
label
string
No
The label text to use in the form control.
labelPlacement
string
No
around
Whether to place the label before, after, or wrapped around the form control. Label text placement can be controlled using aroundLeft or aroundRight.
prepend
string
No
String to prepend to the form control. Useful to wrap the form control with HTML tags.
append
string
No
String to append to the form control. Useful to wrap the form control with HTML tags.
prependToLabel
string
No
String to prepend to the form control's label. Useful to wrap the form control with HTML tags.
appendToLabel
string
No
String to append to the form control's label. Useful to wrap the form control with HTML tags.
encode
any
No
true
Use this argument to decide whether the output of the function should be encoded in order to prevent Cross Site Scripting (XSS) attacks. Set it to true to encode all relevant output for the specific HTML element in question (e.g. tag content, attribute values, and URLs). For HTML elements that have both tag content and attribute values you can set this argument to attributes to only encode attribute values and not tag content.
// 1. Basic search field with a label and pre-populated value
#searchFieldTag(name="q", value=params.q, label="Search")#
// 2. Search field with a placeholder and CSS class (extra HTML attributes are passed through)
#searchFieldTag(name="keywords", label="Keywords", placeholder="Enter keywords...", class="search-input")#
// 3. Search field without a label, value carried from params
#searchFieldTag(name="q", value=params.q)#
Builds and returns a string containing one select form control for the seconds of a minute based on the supplied name.
Name
Type
Required
Default
Description
name
string
Yes
Name to populate in tag's name attribute.
selected
string
No
The second that should be selected initially.
secondStep
numeric
No
1
Pass in 10 to only show seconds 10, 20, 30, etc.
includeBlank
any
No
false
Whether to include a blank option in the select form control. Pass true to include a blank line or a string that should represent what display text should appear for the empty value (for example, "- Select One -").
label
string
No
The label text to use in the form control.
labelPlacement
string
No
around
Whether to place the label before, after, or wrapped around the form control. Label text placement can be controlled using aroundLeft or aroundRight.
prepend
string
No
String to prepend to the form control. Useful to wrap the form control with HTML tags.
append
string
No
String to append to the form control. Useful to wrap the form control with HTML tags.
prependToLabel
string
No
String to prepend to the form control's label. Useful to wrap the form control with HTML tags.
appendToLabel
string
No
String to append to the form control's label. Useful to wrap the form control with HTML tags.
encode
any
No
true
Use this argument to decide whether the output of the function should be encoded in order to prevent Cross Site Scripting (XSS) attacks. Set it to true to encode all relevant output for the specific HTML element in question (e.g. tag content, attribute values, and URLs). For HTML elements that have both tag content and attribute values you can set this argument to attributes to only encode attribute values and not tag content.
// 1. Basic usage — render a seconds select for a countdown form
#secondSelectTag(name="secondsToLaunch", selected=params.secondsToLaunch)#
// 2. Only show 15-second intervals (0, 15, 30, 45)
#secondSelectTag(name="secondsToLaunch", selected=params.secondsToLaunch, secondStep=15)#
// 3. Include a blank option and wrap with a label
#secondSelectTag(name="second", selected=params.second, includeBlank=true, label="Second")#
Builds and returns a string containing a select form control based on the supplied objectName and property.
Note: Pass any additional arguments like class, rel, and id, and the generated tag will also include those values as HTML attributes.
Name
Type
Required
Default
Description
objectName
any
Yes
The variable name of the object to build the form control for.
property
string
Yes
The name of the property to use in the form control.
association
string
No
The name of the association that the property is located on. Used for building nested forms that work with nested properties. If you are building a form with deep nesting, simply pass in a list to the nested object, and Wheels will figure it out.
position
string
No
The position used when referencing a hasMany relationship in the association argument. Used for building nested forms that work with nested properties. If you are building a form with deep nestings, simply pass in a list of positions, and Wheels will figure it out.
options
any
No
A collection to populate the select form control with. Can be a query recordSet or an array of objects.
includeBlank
any
No
false
Whether to include a blank option in the select form control. Pass true to include a blank line or a string that should represent what display text should appear for the empty value (for example, "- Select One -").
valueField
string
No
The column or property to use for the value of each list element. Used only when a query or array of objects has been supplied in the options argument. Required when specifying textField
textField
string
No
The column or property to use for the value of each list element that the end user will see. Used only when a query or array of objects has been supplied in the options argument. Required when specifying valueField
label
string
No
useDefaultLabel
The label text to use in the form control.
labelPlacement
string
No
around
Whether to place the label before, after, or wrapped around the form control. Label text placement can be controlled using aroundLeft or aroundRight.
prepend
string
No
String to prepend to the form control. Useful to wrap the form control with HTML tags.
append
string
No
String to append to the form control. Useful to wrap the form control with HTML tags.
prependToLabel
string
No
String to prepend to the form control's label. Useful to wrap the form control with HTML tags.
appendToLabel
string
No
String to append to the form control's label. Useful to wrap the form control with HTML tags.
errorElement
string
No
span
HTML tag to wrap the form control with when the object contains errors.
errorClass
string
No
field-with-errors
The class name of the HTML tag that wraps the form control when there are errors.
encode
any
No
true
Use this argument to decide whether the output of the function should be encoded in order to prevent Cross Site Scripting (XSS) attacks. Set it to true to encode all relevant output for the specific HTML element in question (e.g. tag content, attribute values, and URLs). For HTML elements that have both tag content and attribute values you can set this argument to attributes to only encode attribute values and not tag content.
// 1. Basic select field bound to a model object property
// Controller
authors = model("Author").findAll(order="lastName");
// View
#select(objectName="book", property="authorId", options=authors)#
// 2. Override which query columns to use for option values and display text
// Controller
authors = model("Author").findAll(order="lastName");
// View
#select(objectName="book", property="authorId", options=authors, valueField="id", textField="fullName")#
// 3. Include a blank/placeholder option at the top of the list
#select(objectName="order", property="statusId", options=statuses, includeBlank="-- Select a Status --")#
// 4. Populate options from a simple list or array instead of a query
#select(objectName="profile", property="country", options="Canada,Mexico,United States")#
// 5. Allow multiple selections (multi-select box)
#select(objectName="post", property="tagIds", options=tags, multiple=true, label="Tags")#
// 6. Nested form — select within a hasMany association loop
// Controller
shipment = model("Shipment").findByKey(params.key, include="orders");
statuses = model("Status").findAll(order="name");
// View
<cfloop from="1" to="#ArrayLen(shipment.orders)#" index="i">
#select(
label = "Order ##shipment.orders[i].orderNumber##",
objectName = "shipment",
association = "orders",
position = i,
property = "statusId",
options = statuses,
includeBlank = true
)#
</cfloop>
Builds and returns a string containing a select form control based on the supplied name and options.
Note: Pass any additional arguments like class, rel, and id, and the generated tag will also include those values as HTML attributes.
Name
Type
Required
Default
Description
name
string
Yes
Name to populate in tag's name attribute.
options
any
Yes
A collection to populate the select form control with. Can be a query recordSet or an array of objects.
selected
string
No
Value of option that should be selected by default.
includeBlank
any
No
false
Whether to include a blank option in the select form control. Pass true to include a blank line or a string that should represent what display text should appear for the empty value (for example, "- Select One -").
multiple
boolean
No
false
Whether to allow multiple selection of options in the select form control.
valueField
string
No
The column or property to use for the value of each list element. Used only when a query or array of objects has been supplied in the options argument. Required when specifying textField
textField
string
No
The column or property to use for the value of each list element that the end user will see. Used only when a query or array of objects has been supplied in the options argument. Required when specifying valueField
label
string
No
The label text to use in the form control.
labelPlacement
string
No
around
Whether to place the label before, after, or wrapped around the form control. Label text placement can be controlled using aroundLeft or aroundRight.
prepend
string
No
String to prepend to the form control. Useful to wrap the form control with HTML tags.
append
string
No
String to append to the form control. Useful to wrap the form control with HTML tags.
prependToLabel
string
No
String to prepend to the form control's label. Useful to wrap the form control with HTML tags.
appendToLabel
string
No
String to append to the form control's label. Useful to wrap the form control with HTML tags.
encode
any
No
true
Use this argument to decide whether the output of the function should be encoded in order to prevent Cross Site Scripting (XSS) attacks. Set it to true to encode all relevant output for the specific HTML element in question (e.g. tag content, attribute values, and URLs). For HTML elements that have both tag content and attribute values you can set this argument to attributes to only encode attribute values and not tag content.
// 1. Basic usage with a simple list of options
#selectTag(name="color", options="Red,Green,Blue")#
// 2. Use a query as the options source, specifying which columns map to value and display text
cities = model("City").findAll(order="name");
#selectTag(name="cityId", options=cities, valueField="id", textField="name")#
// 3. Pre-select a value and include a blank "please choose" option
#selectTag(name="cityId", options=cities, valueField="id", textField="name", selected=params.cityId, includeBlank="- Select a City -")#
// 4. Allow multiple selections
#selectTag(name="tagIds", options=model("Tag").findAll(order="name"), valueField="id", textField="name", multiple=true)#
Sends an email using a template and an optional layout to wrap it in.
Besides the Wheels-specific arguments documented here, you can also pass in any argument that is accepted by the cfmail tag as well as your own arguments to be used by the view.
Note that only arguments whose names match a known cfmail attribute are passed through to cfmail; every other argument is made available to the email view as a variable instead.
Name
Type
Required
Default
Description
template
string
No
The path to the email template or two paths if you want to send a multipart email (a maximum of two templates, one text and one html version, is supported). if the detectMultipart argument is false, the template for the text version should be the first one in the list. This argument is also aliased as templates.
from
string
Yes
Email address to send from.
to
string
Yes
List of email addresses to send the email to.
subject
string
Yes
The subject line of the email.
layout
any
No
false
Layout(s) to wrap the email template in. This argument is also aliased as layouts.
file
string
No
A list of the names of the files to attach to the email. This will reference files stored in the files folder (or a path relative to it). This argument is also aliased as files.
detectMultipart
boolean
No
true
When set to true and multiple values are provided for the template argument, Wheels will detect which of the templates is text and which one is HTML (by counting the < characters).
deliver
boolean
No
true
When set to false, the email will not be sent.
writeToFile
string
No
The file to which the email contents will be written
// 1. Send a welcome email to a new member, passing custom variables to the template
newMember = model("Member").findByKey(params.member.id);
sendEmail(
from="welcome@example.com",
to=newMember.email,
subject="Thank You for Becoming a Member",
template="welcomeEmail",
recipientName=newMember.name,
startDate=newMember.startDate
);
// 2. Send a multipart email (text + HTML) using two templates
sendEmail(
from="news@example.com",
to=params.subscriber.email,
subject="Your Weekly Newsletter",
template="newsletterText,newsletterHtml",
layout=false,
issueDate=Now()
);
// 3. Send an email with a file attachment and suppress actual delivery (e.g. during testing)
sendEmail(
from="billing@example.com",
to=params.customer.email,
subject="Your Invoice",
template="invoiceEmail",
file="invoice_2024.pdf",
deliver=false
);
Sends a file to the user (from the files folder or a path relative to it by default).
Name
Type
Required
Default
Description
file
string
Yes
The file to send to the user. Values containing the .. character sequence anywhere (even as part of a legitimate file name) are rejected to prevent path traversal.
name
string
No
The file name to show in the browser download dialog box.
type
string
No
The HTTP content type to deliver the file as.
disposition
string
No
attachment
Set to inline to have the browser handle the opening of the file (possibly inline in the browser) or set to attachment to force a download dialog box.
directory
string
No
Directory outside of the web root where the file exists. Must be a full path. Values containing the .. character sequence are rejected to prevent path traversal.
deleteFile
boolean
No
false
Pass in true to delete the file on the server after sending it.
deliver
boolean
No
true
When set to false, the file will not be sent to the browser (used for testing).
// 1. Send a PDF file to the user from the default files folder
sendFile(file="wheels_tutorial_20081028_J657D6HX.pdf");
// 2. Send the same file but give the user a friendlier name in the browser download dialog
sendFile(file="wheels_tutorial_20081028_J657D6HX.pdf", name="Tutorial.pdf");
// 3. Display the file inline in the browser instead of forcing a download dialog
sendFile(file="report.pdf", disposition="inline");
// 4. Send a file with an explicit MIME type
sendFile(file="export.csv", type="text/csv", name="data-export.csv");
// 5. Send a file located outside of the web root using an absolute directory path
sendFile(file="invoice_2024_001.pdf", directory="/var/app/private/invoices");
// 6. Send a file and delete it from the server after delivery (e.g., a temporary export)
sendFile(file="temp_export_J657D6HX.csv", name="export.csv", deleteFile=true);
// 7. Send a file stored in the RAM virtual file system
sendFile(file="ram://generated_report.pdf", name="report.pdf");
sendSSEComment()
void
controller
Send an SSE comment (keep-alive ping) through a streaming writer.
Name
Type
Required
Default
Description
writer
any
Yes
The writer object returned by initSSEStream().
comment
string
No
ping
Optional comment text.
sendSSEEvent()
void
controller
Send an SSE event through a streaming writer obtained from initSSEStream().
Name
Type
Required
Default
Description
writer
any
Yes
The writer object returned by initSSEStream().
data
string
Yes
The event data to send.
event
string
No
Optional event type name.
id
string
No
Optional event ID.
retry
numeric
No
0
Optional reconnection time in milliseconds.
service()
any
controller
model
mapper
migrator
migration
tabledefinition
Resout un composant de la couche de services.
Les services portent la logique metier et l'acces aux donnees ; les
controleurs ne font plus que traduire HTTP <-> domaine. Un service ne
connait ni params, ni les entetes, ni le format de sortie.
Les dependances sont injectees plutot que resolues a l'interieur : model
et le nom du datasource sont des fonctions et des reglages de Wheels,
indisponibles dans un composant instancie a la main. Les passer rend aussi
le service testable en isolation, avec un double a la place de model.
model est enveloppe dans une FERMETURE et non passe par reference : une
fonction melangee dans le controleur, extraite puis appelee depuis un
autre composant, perd la portee variables de son proprietaire et casse
sur les appels internes de Wheels ($cachedModelLookup). La fermeture, elle,
capture la portee lexicale du controleur.
Le cache est de portee REQUETE et non application : le contrat (§8)
interdit de compter sur un etat en memoire entre requetes, et une portee
application obligerait a verrouiller et a purger au rechargement du
framework. Instancier un composant est negligeable a cote d'une requete SQL.
Name
Type
Required
Default
Description
name
string
Yes
// 1. Resolve a registered service and call a method on it
mailer = service("MailerService");
mailer.send(to="user@example.com", subject="Welcome!");
// 2. Resolve a payment gateway service and process a charge
gateway = service("PaymentGateway");
result = gateway.charge(amount=params.amount, token=params.stripeToken);
// 3. Use a service in a model callback to send a notification
component extends="Model" {
function config() {
afterCreate(method="notifyAdmin");
}
private function notifyAdmin() {
notifier = service("NotificationService");
notifier.notify(event="userCreated", userId=this.id);
}
}
Use to configure a global setting or set a default for a function.
// 1. Set the `URLRewriting` global setting to `Partial`.
set(URLRewriting="Partial");
// 2. Set default argument values for the `buttonTo` view helper.
// This pattern works for most Wheels helper functions and their arguments.
set(functionName="buttonTo", onlyPath=true, host="", protocol="", port=0, text="", confirm="", image="", disable="");
// 3. Set default values for the `textField` form helper to control label placement and wrapping markup.
set(functionName="textField", labelPlacement="before", prependToLabel="<div>", append="</div>", appendToLabel="<br>");
// 4. Apply the same defaults to multiple helper functions at once by passing a comma-delimited list to `functionName`.
set(functionName="textField,passwordField,textArea", labelPlacement="before");
Use this function if you need a more low level way of setting the entire filter chain for a controller.
Name
Type
Required
Default
Description
chain
array
Yes
An array of structs, each of which represent an argumentCollection that get passed to the filters function. This should represent the entire filter chain that you want to use for this controller.
// 1. Set the entire filter chain directly using an array of structs
setFilterChain([
{through="restrictAccess"},
{through="isLoggedIn, checkIPAddress", except="home, login"},
{type="after", through="logConversion", only="thankYou"}
]);
// 2. Replace an inherited filter chain in a child controller by starting fresh
// In app/controllers/Admin.cfc config():
parentChain = filterChain();
// Modify parentChain as needed, then reassign it wholesale
setFilterChain([
{through="requireAdmin"},
{through="loadCurrentUser", except="login"},
{type="after", through="auditAction"}
]);
// 3. Conditionally swap the filter chain based on application mode
if (get("environment") == "testing") {
setFilterChain([
{through="stubAuthentication"}
]);
} else {
setFilterChain([
{through="requireSSL"},
{through="authenticate"},
{type="after", through="trackPageView"}
]);
}
Dynamically sets flashStorage during request lifecycle.
Name
Type
Required
Default
Description
storage
string
No
session
Accepts "session" or "cookie"
setGlobally
boolean
No
false
If true, updates both app-level and controller-level flashStorage
// 1. Switch flash storage to cookie for the current request only
setFlashStorage(storage="cookie");
flashInsert(notice="Switching to cookie-based flash.");
// 2. Switch flash storage to session (the default) for the current request only
setFlashStorage(storage="session");
// 3. Update flash storage globally so all subsequent requests also use cookie storage
setFlashStorage(storage="cookie", setGlobally=true);
Allows you to pass in the name(s) of the property(s) that should be used as the primary key(s).
Pass as a list if defining a composite primary key.
This function is also aliased as setPrimaryKeys().
Name
Type
Required
Default
Description
property
string
Yes
Property (or list of properties) to set as the primary key.
// 1. In `models/User.cfc`, define the primary key as a column called `userID`
// instead of the Wheels default of `id`.
component extends="Model" {
function config() {
setPrimaryKey("userID");
}
}
// 2. Define a composite primary key for a join model using two columns.
// `setPrimaryKeys()` is an alias for `setPrimaryKey()` that reads more
// naturally when multiple properties are involved.
component extends="Model" {
function config() {
table("users_roles");
setPrimaryKeys("userID,roleID");
}
}
Alias for setPrimaryKey().
Use this for better readability when you're setting multiple properties as the primary key.
Name
Type
Required
Default
Description
property
string
Yes
Property (or list of properties) to set as the primary key.
// 1. In `models/Subscription.cfc`, define the primary key as a composite of `customerId` and `publicationId`.
component extends="Model" {
function config() {
setPrimaryKeys("customerId,publicationId");
}
}
// 2. In `models/OrderItem.cfc`, define a composite primary key using three columns.
component extends="Model" {
function config() {
setPrimaryKeys("orderId,productId,warehouseId");
}
}
Allows you to set all the properties of an object at once by passing in a structure with keys matching the property names.
Name
Type
Required
Default
Description
properties
struct
No
[runtime expression]
The properties you want to set on the object (can also be passed in as named arguments).
// 1. Update properties from a form post struct
user = model("User").findByKey(1);
user.setProperties(params.user);
// 2. Pass a struct literal directly to set multiple properties at once
user = model("User").findByKey(1);
user.setProperties({firstName: "Jane", lastName: "Doe", email: "jane@example.com"});
user.save();
// 3. Use named arguments instead of a struct (named args are merged with the properties struct)
user = model("User").findByKey(1);
user.setProperties(firstName="John", lastName="Smith");
user.save();
Sets content that Wheels will send to the client in response to the request.
Name
Type
Required
Default
Description
content
string
Yes
The content to send to the client.
// 1. Override the response body with a plain string
setResponse("Maintenance mode active. Please try again later.");
// 2. Modify an already-rendered response in an after filter
// (e.g. append a debug comment to every HTML response)
private void function appendDebugComment() {
current = response();
setResponse(current & "<!-- rendered at #Now()# -->");
}
// 3. Replace the response with serialized data after a renderView() call
// (useful in tests or custom middleware-style filters)
setResponse(serializeJSON({status: "ok", timestamp: Now()}));
Sets a prefix to prepend to the table name when this model runs SQL queries.
Name
Type
Required
Default
Description
prefix
string
Yes
A prefix to prepend to the table name.
// 1. In `models/User.cfc`, prepend `tbl` to the default table name so
// Wheels queries the `tblusers` table instead of `users`.
function config() {
setTableNamePrefix("tbl");
}
// 2. Use a schema-style prefix to namespace legacy tables shared across
// multiple applications on the same database.
// models/Order.cfc
component extends="Model" {
function config() {
setTableNamePrefix("legacy_");
// Wheels will now query `legacy_orders` for this model.
}
}
Use this function if you need a more low level way of setting the entire verification chain for a controller.
Name
Type
Required
Default
Description
chain
array
Yes
An array of structs, each of which represent an argumentCollection that get passed to the verifies function. This should represent the entire verification chain that you want to use for this controller.
// 1. Set the entire verification chain directly using an array of structs
setVerificationChain([
{only="handleForm", post=true},
{only="edit", get=true, params="userId", paramsTypes="integer"},
{only="delete", post=true, session="currentUser", handler="accessDenied"}
]);
// 2. Get the existing chain, modify it, and set it back
chain = verificationChain();
ArrayAppend(chain, {only="create,update", post=true});
setVerificationChain(chain);
// 3. Replace the chain built by a parent controller with a stricter one
setVerificationChain([
{except="index,show", post=true, session="isAdmin", handler="requireAdmin"},
{only="destroy", post=true}
]);
Marks this model as shared — it will always use the default application datasource
even when a tenant is active. Use this for models like Tenant, Plan, or any
lookup table that lives in the central database rather than per-tenant databases.
// 1. Mark the Tenant model as shared so it always reads from the central database
// models/Tenant.cfc
component extends="Model" {
function config() {
sharedModel();
}
}
// 2. Use sharedModel() for lookup tables that live in the central database,
// not in per-tenant databases
// models/Plan.cfc
component extends="Model" {
function config() {
sharedModel();
// All finder calls on Plan will use the default application datasource
// regardless of which tenant is currently active.
}
}
Returns formatted text using HTML break tags ( ) and HTML paragraph elements () based on the newline characters and carriage returns in the text that is passed in.
Name
Type
Required
Default
Description
text
string
Yes
The text to format.
wrap
boolean
No
true
Set to true to wrap the result in a paragraph HTML element.
encode
boolean
No
true
Use this argument to decide whether the output of the function should be encoded in order to prevent Cross Site Scripting (XSS) attacks. Set it to true to encode all relevant output for the specific HTML element in question (e.g. tag content, attribute values, and URLs). For HTML elements that have both tag content and attribute values you can set this argument to attributes to only encode attribute values and not tag content.
// 1. Format a blog post body — newlines become <br> and blank lines become paragraph breaks
writeOutput(simpleFormat(post.bodyText));
// A single newline becomes <br>
// A blank line (two newlines) becomes </p><p>
// The result is wrapped in <p>...</p> by default
// 2. Demonstrate the HTML output with literal input
text = "I love this post!" & Chr(10) & Chr(10) & "Here's why:" & Chr(10) & "* Short" & Chr(10) & "* Succinct";
writeOutput(simpleFormat(text));
// -> <p>I love this post!</p>
//
// <p>Here's why:<br>
// * Short<br>
// * Succinct</p>
// 3. Skip the wrapping paragraph tag (wrap=false) when you are composing markup yourself
writeOutput("<div>" & simpleFormat(text=post.excerpt, wrap=false) & "</div>");
// 4. Disable XSS encoding when the text is already trusted/pre-encoded HTML
writeOutput(simpleFormat(text=post.bodyText, encode=false));
Builds and returns a string containing the opening form tag.
The form's action will be built according to the same rules as URLFor.
Note: Pass any additional arguments like class, rel, and id, and the generated tag will also include those values as HTML attributes.
Name
Type
Required
Default
Description
method
string
No
post
The type of method to use in the form tag (delete, get, patch, post, and put are the options).
multipart
boolean
No
false
Set to true if the form should be able to upload files.
route
string
No
Name of a route that you have configured in config/routes.cfm.
controller
string
No
Name of the controller to include in the URL.
action
string
No
Name of the action to include in the URL.
key
any
No
Key(s) to include in the URL.
params
string
No
Any additional parameters to be set in the query string (example: wheels=cool&x=y). Please note that Wheels uses the & and = characters to split the parameters and encode them properly for you. However, if you need to pass in & or = as part of the value, then you need to encode them (and only them), example: a=cats%26dogs%3Dtrouble!&b=1.
anchor
string
No
Sets an anchor name to be appended to the path.
onlyPath
boolean
No
true
If true, returns only the relative URL (no protocol, host name or port).
host
string
No
Set this to override the current host.
protocol
string
No
Set this to override the current protocol.
port
numeric
No
0
Set this to override the current port number.
prepend
string
No
String to prepend to the form control. Useful to wrap the form control with HTML tags.
append
string
No
String to append to the form control. Useful to wrap the form control with HTML tags.
encode
any
No
true
Use this argument to decide whether the output of the function should be encoded in order to prevent Cross Site Scripting (XSS) attacks. Set it to true to encode all relevant output for the specific HTML element in question (e.g. tag content, attribute values, and URLs). For HTML elements that have both tag content and attribute values you can set this argument to attributes to only encode attribute values and not tag content.
// 1. Basic form posting to a specific action in the current controller
#startFormTag(action="create")#
<!--- form controls go here --->
#endFormTag()#
// 2. Form targeting a different controller and action, using HTTP GET
#startFormTag(controller="search", action="results", method="get")#
<!--- search fields --->
#endFormTag()#
// 3. Multipart form for file uploads
#startFormTag(action="upload", multipart=true)#
<!--- file input and other controls --->
#endFormTag()#
// 4. Form using a named route with extra HTML attributes (id, class)
#startFormTag(route="newRegistration", id="registration-form", class="form-horizontal")#
<!--- registration fields --->
#endFormTag()#
// 5. Form that sends a PUT request (e.g., update an existing record)
#startFormTag(action="update", key=params.key, method="put")#
<!--- edit fields --->
#endFormTag()#
// 1. Add a single string column to a new table
t = createTable(name='users');
t.string(columnNames='username', limit=100, allowNull=false);
t.string(columnNames='email', limit=255, allowNull=false);
t.timestamps();
t.create();
// 2. Add multiple string columns at once with a default value
t = createTable(name='products');
t.string(columnNames='name,sku,status', limit=100, default='', allowNull=false);
t.integer(columnNames='stock', default=0, allowNull=false);
t.timestamps();
t.create();
// 3. Alter an existing table to add a string column with a limit
t = changeTable(name='orders');
t.string(columnNames='trackingNumber', limit=50, allowNull=true);
t.change();
Removes all links from an HTML string, leaving just the link text.
Name
Type
Required
Default
Description
html
string
Yes
The HTML to remove links from.
encode
boolean
No
true
Use this argument to decide whether the output of the function should be encoded in order to prevent Cross Site Scripting (XSS) attacks. Set it to true to encode all relevant output for the specific HTML element in question (e.g. tag content, attribute values, and URLs). For HTML elements that have both tag content and attribute values you can set this argument to attributes to only encode attribute values and not tag content.
// 1. Remove a link from an HTML string, leaving only the link text
result = stripLinks('<strong>Wheels</strong> is a framework for <a href="http://www.adobe.com/products/coldfusion">ColdFusion</a>.');
// result -> "<strong>Wheels</strong> is a framework for ColdFusion."
// 2. Strip multiple links from a string
result = stripLinks('Visit <a href="https://cfwheels.org">CFWheels</a> or read the <a href="https://cfwheels.org/docs">docs</a> for more info.');
// result -> "Visit CFWheels or read the docs for more info."
// 3. Strip links while skipping XSS encoding (e.g. when you trust the source HTML)
result = stripLinks('<p>Check out <a href="https://cfwheels.org">CFWheels</a>!</p>', encode=false);
// result -> "<p>Check out CFWheels!</p>"
Use this argument to decide whether the output of the function should be encoded in order to prevent Cross Site Scripting (XSS) attacks. Set it to true to encode all relevant output for the specific HTML element in question (e.g. tag content, attribute values, and URLs). For HTML elements that have both tag content and attribute values you can set this argument to attributes to only encode attribute values and not tag content.
// 1. Strip all HTML tags from a string, leaving plain text
result = stripTags('<strong>CFWheels</strong> is a framework for <a href="http://www.adobe.com/products/coldfusion">ColdFusion</a>.');
// result -> "CFWheels is a framework for ColdFusion."
// 2. Strip tags from a richer HTML fragment
result = stripTags('<h1>Welcome</h1><p>This is a <em>great</em> framework.</p>');
// result -> "WelcomeThis is a great framework."
// 3. Strip tags while skipping XSS encoding (e.g. when you trust the source HTML)
result = stripTags('<p>Hello, <strong>world</strong>!</p>', encode=false);
// result -> "Hello, world!"
Returns a link tag for a stylesheet (or several) based on the supplied arguments.
Name
Type
Required
Default
Description
sources
string
No
The name of one or many CSS files in the stylesheets folder, minus the .css extension. Pass a full URL to generate a tag for an external style sheet. Can also be called with the source argument.
type
string
No
text/css
The type attribute for the link tag.
media
string
No
all
The media attribute for the link tag.
rel
string
No
The rel attribute for the relation between the tag and href.
head
boolean
No
false
Set to true to place the output in the head area of the HTML page instead of the default behavior (which is to place the output where the function is called from).
delim
string
No
,
The delimiter to use for the list of CSS files.
encode
boolean
No
true
Use this argument to decide whether the output of the function should be encoded in order to prevent Cross Site Scripting (XSS) attacks. Set it to true to encode all relevant output for the specific HTML element in question (e.g. tag content, attribute values, and URLs). For HTML elements that have both tag content and attribute values you can set this argument to attributes to only encode attribute values and not tag content.
// 1. Include a single stylesheet from the `stylesheets` folder
// Generates: <link rel="stylesheet" href="/stylesheets/app.css" ...>
writeOutput(styleSheetLinkTag("app"));
// 2. Include multiple stylesheets with a comma-delimited list
// Generates two separate <link> tags for blog.css and comments.css
writeOutput(styleSheetLinkTag("blog,comments"));
// 3. Include a stylesheet for print media only
writeOutput(styleSheetLinkTag(sources="print", media="print"));
// 4. Include an external stylesheet via full URL
writeOutput(styleSheetLinkTag("https://fonts.googleapis.com/css2?family=Roboto"));
// 5. Push a stylesheet into the <head> from anywhere in the view
// The tag is injected into the <head> section rather than rendered inline
writeOutput(styleSheetLinkTag(sources="tabs", head=true));
// 6. Use a pipe delimiter instead of the default comma
writeOutput(styleSheetLinkTag(sources="reset|layout|theme", delim="|"));
Builds and returns a string containing a submit button form control.
Note: Pass any additional arguments like class, rel, and id, and the generated tag will also include those values as HTML attributes.
Name
Type
Required
Default
Description
value
string
No
Save changes
Message to display in the button form control.
image
string
No
File name of the image file to use in the button form control.
prepend
string
No
String to prepend to the form control. Useful to wrap the form control with HTML tags.
append
string
No
String to append to the form control. Useful to wrap the form control with HTML tags.
encode
any
No
true
Use this argument to decide whether the output of the function should be encoded in order to prevent Cross Site Scripting (XSS) attacks. Set it to true to encode all relevant output for the specific HTML element in question (e.g. tag content, attribute values, and URLs). For HTML elements that have both tag content and attribute values you can set this argument to attributes to only encode attribute values and not tag content.
// 1. Basic submit button inside a form
#startFormTag(action="save")#
<!--- form controls go here --->
#submitTag()#
#endFormTag()#
// -> <form action="/products/save" method="post">...<input type="submit" value="Save changes"></form>
// 2. Custom button label
#submitTag(value="Create Account")#
// -> <input type="submit" value="Create Account">
// 3. Image submit button
#submitTag(image="submit-button.png")#
// -> <input type="image" src="/images/submit-button.png">
// 4. Submit button with extra HTML attributes (class and id)
#submitTag(value="Place Order", class="btn btn-primary", id="order-submit")#
// -> <input type="submit" value="Place Order" class="btn btn-primary" id="order-submit">
// 5. Submit button wrapped with HTML using prepend and append
#submitTag(value="Save", prepend="<div class=""actions"">", append="</div>")#
// -> <div class="actions"><input type="submit" value="Save"></div>
subscribeToChannel()
void
controller
Subscribe to a channel and stream events to the client via SSE.
Opens a long-lived SSE connection that delivers matching events
until the client disconnects or the timeout is reached.
For the "memory" adapter, subscribes to the in-memory Channel
engine and buffers events for delivery. For the "database" adapter,
polls the wheels_events table at regular intervals.
Name
Type
Required
Default
Description
channel
string
Yes
The channel name to subscribe to (e.g. "user.42").
events
string
No
Comma-delimited list of event types to filter. Empty = all events.
lastEventId
string
No
Resume from this event ID. Auto-detected from Last-Event-ID header if empty.
adapter
string
No
"memory" (default) or "database".
pollInterval
numeric
No
2
Seconds between polls for database adapter (default 2).
timeout
numeric
No
300
Maximum connection duration in seconds (default 300 = 5 minutes).
Calculates the sum of values for a given property.
Uses the SQL function SUM.
If no records can be found to perform the calculation on you can use the ifNull argument to decide what should be returned.
Name
Type
Required
Default
Description
property
string
Yes
Name of the property to get the sum for (must be a property of a numeric data type).
where
string
No
Maps to the WHERE clause of the query (or HAVING when necessary). The following operators are supported: =, !=, <>, <, <=, >, >=, LIKE, NOT LIKE, IN, NOT IN, IS NULL, IS NOT NULL, AND, and OR (note that the key words need to be written in upper case). You can also use parentheses to group statements. Nested queries not allowed. You do not need to specify the table name(s); Wheels will do that for you.
include
string
No
Associations that should be included in the query using INNER or LEFT OUTER joins (which join type that is used depends on how the association has been set up in your model). If all included associations are set on the current model, you can specify them in a list (e.g. department,addresses,emails). You can build more complex include strings by using parentheses when the association is set on an included model, like album(artist(genre)), for example. These complex include strings only work when returnAs is set to query though.
distinct
boolean
No
false
When true, SUM returns the sum of unique values only.
parameterize
any
No
true
Set to true to use cfqueryparam on all columns, or pass in a list of property names to use cfqueryparam on those only.
ifNull
any
No
The value returned if no records are found. Common usage is to set this to 0 to make sure a numeric value is always returned instead of a blank string.
includeSoftDeletes
boolean
No
false
Set to true to include soft-deleted records in the queries that this method runs.
group
string
No
Maps to the GROUP BY clause of the query. You do not need to specify the table name(s); Wheels will do that for you.
// 1. Get the sum of all salaries
allSalaries = model("Employee").sum("salary");
// 2. Get the sum of all salaries for employees in a given country
allAustralianSalaries = model("Employee").sum(property="salary", include="country", where="countryname='Australia'");
// 3. Make sure a numeric value is always returned, even if there are no records analyzed by the query
salarySum = model("Employee").sum(property="salary", where="salary BETWEEN #params.min# AND #params.max#", ifNull=0);
// 4. Sum only distinct (unique) salary values
distinctSum = model("Employee").sum(property="salary", distinct=true);
// 5. Get the total salary per department using grouping
byDepartment = model("Employee").sum(property="salary", group="departmentId");
// byDepartment is a query with columns: departmentId, salarysum
Switches the active tenant mid-request. Throws if the current tenant is locked
(set by TenantResolver middleware) unless force is true.
Name
Type
Required
Default
Description
tenant
struct
Yes
Struct with at minimum a dataSource key. Optional: id, config.
force
boolean
No
false
If true, overrides the lock set by TenantResolver middleware.
// 1. Switch to a different tenant mid-request (minimal required argument)
switchTenant(tenant = {dataSource = "tenant_db_acme"});
// 2. Switch with a full tenant struct (id and per-tenant config override)
switchTenant(
tenant = {
dataSource = "tenant_db_beta",
id = "beta",
config = {timeZone = "America/New_York"}
}
);
// 3. Force-switch even when the current tenant is locked by TenantResolver middleware
switchTenant(
tenant = {dataSource = "tenant_db_admin", id = "admin"},
force = true
);
Use this method to tell Wheels what database table to connect to for this model.
You only need to use this method when your table naming does not follow the standard Wheels convention of a singular object name mapping to a plural table name.
To not use a table for your model at all, call table(false).
Name
Type
Required
Default
Description
name
any
Yes
Name of the table to map this model to.
// 1. Map the `User` model to a non-standard table name.
// In models/User.cfc
function config() {
// Tell Wheels to use `tbl_USERS` instead of the default `users` table.
table("tbl_USERS");
}
// 2. Map a model to a table with a legacy prefix.
// In models/Product.cfc
function config() {
table("legacy_products");
}
// 3. Declare a model that has no backing database table at all.
// In models/ApiResponse.cfc
function config() {
table(false);
}
Returns the name of the database table that this model is mapped to.
This is a getter and takes no arguments — the table setter is table().
Calling tableName() with an argument has always been a silent no-op (CFML
accepts the extra argument and the model keeps its convention table), a trap
some 4.0-era docs taught as a setter. When error information is shown
(development / testing — the same gate exists() uses above) it now fails
loud; in production it stays a no-op so an upgrade never breaks a running
app. See issue #3079.
// 1. Check what table the User model is mapped to (Wheels convention: singular model -> plural table)
name = model("User").tableName();
// name -> "users"
// 2. Check the table for a model that uses a custom table name (set via table() in config())
// In models/StaffMember.cfc: table("employees");
name = model("StaffMember").tableName();
// name -> "employees"
// 3. Use the table name dynamically in a log message or custom SQL fragment
tableUsed = model("Order").tableName();
writeOutput("Querying table: " & tableUsed);
// Outputs: Querying table: orders
Builds and returns a string containing a telephone field form control based on the supplied objectName and property.
Note: Pass any additional arguments like class, rel, and id, and the generated tag will also include those values as HTML attributes.
Name
Type
Required
Default
Description
objectName
any
Yes
The variable name of the object to build the form control for.
property
string
Yes
The name of the property to use in the form control.
association
string
No
The name of the association that the property is located on. Used for building nested forms that work with nested properties. If you are building a form with deep nesting, simply pass in a list to the nested object, and Wheels will figure it out.
position
string
No
The position used when referencing a hasMany relationship in the association argument. Used for building nested forms that work with nested properties. If you are building a form with deep nestings, simply pass in a list of positions, and Wheels will figure it out.
label
string
No
useDefaultLabel
The label text to use in the form control.
labelPlacement
string
No
around
Whether to place the label before, after, or wrapped around the form control. Label text placement can be controlled using aroundLeft or aroundRight.
prepend
string
No
String to prepend to the form control. Useful to wrap the form control with HTML tags.
append
string
No
String to append to the form control. Useful to wrap the form control with HTML tags.
prependToLabel
string
No
String to prepend to the form control's label. Useful to wrap the form control with HTML tags.
appendToLabel
string
No
String to append to the form control's label. Useful to wrap the form control with HTML tags.
errorElement
string
No
span
HTML tag to wrap the form control with when the object contains errors.
errorClass
string
No
field-with-errors
The class name of the HTML tag that wraps the form control when there are errors.
encode
any
No
true
Use this argument to decide whether the output of the function should be encoded in order to prevent Cross Site Scripting (XSS) attacks. Set it to true to encode all relevant output for the specific HTML element in question (e.g. tag content, attribute values, and URLs). For HTML elements that have both tag content and attribute values you can set this argument to attributes to only encode attribute values and not tag content.
<!--- Provide a `label` and the required `objectName` and `property` --->\n#telField(label="Phone Number", objectName="contact", property="phone")#\n\n<!--- Add a CSS class and a placeholder for formatting guidance --->\n#telField(label="Mobile", objectName="user", property="mobile", class="tel-input", placeholder="+1-555-000-0000")#\n\n<!--- Render telephone fields for each phone number in a nested association --->\n<fieldset>\n\t<legend>Phone Numbers</legend>\n\t<cfloop from="1" to="#ArrayLen(contact.phoneNumbers)#" index="i">\n\t\t#telField(label="Phone ##i#", objectName="contact", association="phoneNumbers", position=i, property="number")#\n\t</cfloop>\n</fieldset>
Builds and returns a string containing a telephone field form control based on the supplied name.
Note: Pass any additional arguments like class, rel, and id, and the generated tag will also include those values as HTML attributes.
Name
Type
Required
Default
Description
name
string
Yes
Name to populate in tag's name attribute.
value
string
No
Value to populate in tag's value attribute.
label
string
No
The label text to use in the form control.
labelPlacement
string
No
around
Whether to place the label before, after, or wrapped around the form control. Label text placement can be controlled using aroundLeft or aroundRight.
prepend
string
No
String to prepend to the form control. Useful to wrap the form control with HTML tags.
append
string
No
String to append to the form control. Useful to wrap the form control with HTML tags.
prependToLabel
string
No
String to prepend to the form control's label. Useful to wrap the form control with HTML tags.
appendToLabel
string
No
String to append to the form control's label. Useful to wrap the form control with HTML tags.
encode
any
No
true
Use this argument to decide whether the output of the function should be encoded in order to prevent Cross Site Scripting (XSS) attacks. Set it to true to encode all relevant output for the specific HTML element in question (e.g. tag content, attribute values, and URLs). For HTML elements that have both tag content and attribute values you can set this argument to attributes to only encode attribute values and not tag content.
// 1. Basic telephone field with a pre-filled value
#telFieldTag(name="phone", value="555-867-5309")#
// 2. Telephone field with a label and a CSS class
#telFieldTag(name="mobilePhone", value="", label="Mobile Phone", class="tel-input")#
// 3. Telephone field with label placement and prepend/append wrappers
#telFieldTag(name="officePhone", label="Office Phone", labelPlacement="before", prepend="<div class=""field"">", append="</div>")#
Returns the current tenant struct, or an empty struct if no tenant is active.
The tenant struct contains: id, dataSource, config, and $locked.
A tenant only counts as active when it carries a non-empty dataSource — the same test
$tenantDataSource() applies before it routes a query. Anything else on the key reads as
no tenant rather than being handed back as though it were a resolved one, so a malformed
value degrades to a no-op instead of wrong behaviour (#3336). Every framework producer
(switchTenant(), TenantResolver, Job.$restoreTenantContext(), TenantMigrator)
already guarantees a non-empty dataSource, so this only filters foreign values.
// 1. Get the active tenant struct (when a tenant is set)
t = tenant();
// t -> {id: "acme", dataSource: "tenant_db_acme", config: {}, $locked: true}
// 2. Check whether a tenant is active before using its properties
t = tenant();
if (!structIsEmpty(t)) {
writeOutput("Current tenant: " & t.id);
} else {
writeOutput("No tenant active — using application defaults.");
}
// 3. Access a per-tenant config value set via switchTenant()
t = tenant();
if (structKeyExists(t, "config") && structKeyExists(t.config, "timeZone")) {
writeOutput("Tenant time zone: " & t.config.timeZone);
}
Adds text columns to table definition.
In MySQL databases, you can specify different text sizes:
- Regular TEXT (65KB) - default when no size is specified
- MEDIUMTEXT (16MB) - specify size="mediumtext"
- LONGTEXT (4GB) - specify size="longtext"
For other database engines, the size parameter is ignored and the default text type is used.
Name
Type
Required
Default
Description
columnNames
string
No
default
any
No
allowNull
boolean
No
size
string
No
// 1. Add a single text column to a table
t.text("body");
// 2. Add multiple text columns at once
t.text("summary,description,notes");
// 3. Add a text column that defaults to an empty string and disallows nulls
t.text(columnNames="bio", default="", allowNull=false);
// 4. Add a MEDIUMTEXT column in MySQL (16MB capacity; ignored on other databases)
t.text(columnNames="content", size="mediumtext");
// 5. Add a LONGTEXT column in MySQL (4GB capacity; ignored on other databases)
t.text(columnNames="rawHtml", size="longtext");
// 6. Full migration example using text() inside createTable
t = createTable("articles");
t.string("title");
t.text("body");
t.text(columnNames="excerpt", allowNull=true);
t.timestamps();
t.create();
Builds and returns a string containing a text area field form control based on the supplied objectName and property.
Note: Pass any additional arguments like class, rel, and id, and the generated tag will also include those values as HTML attributes.
Name
Type
Required
Default
Description
objectName
any
Yes
The variable name of the object to build the form control for.
property
string
Yes
The name of the property to use in the form control.
association
string
No
The name of the association that the property is located on. Used for building nested forms that work with nested properties. If you are building a form with deep nesting, simply pass in a list to the nested object, and Wheels will figure it out.
position
string
No
The position used when referencing a hasMany relationship in the association argument. Used for building nested forms that work with nested properties. If you are building a form with deep nestings, simply pass in a list of positions, and Wheels will figure it out.
label
string
No
useDefaultLabel
The label text to use in the form control.
labelPlacement
string
No
around
Whether to place the label before, after, or wrapped around the form control. Label text placement can be controlled using aroundLeft or aroundRight.
prepend
string
No
String to prepend to the form control. Useful to wrap the form control with HTML tags.
append
string
No
String to append to the form control. Useful to wrap the form control with HTML tags.
prependToLabel
string
No
String to prepend to the form control's label. Useful to wrap the form control with HTML tags.
appendToLabel
string
No
String to append to the form control's label. Useful to wrap the form control with HTML tags.
errorElement
string
No
span
HTML tag to wrap the form control with when the object contains errors.
errorClass
string
No
field-with-errors
The class name of the HTML tag that wraps the form control when there are errors.
encode
any
No
true
Use this argument to decide whether the output of the function should be encoded in order to prevent Cross Site Scripting (XSS) attacks. Set it to true to encode all relevant output for the specific HTML element in question (e.g. tag content, attribute values, and URLs). For HTML elements that have both tag content and attribute values you can set this argument to attributes to only encode attribute values and not tag content.
// 1. Basic text area bound to a model object property
#textArea(objectName="article", property="overview", label="Overview")#
// 2. Text area with custom rows/cols attributes and a placeholder
#textArea(objectName="post", property="body", label="Body", rows=10, cols=60, placeholder="Write your post here...")#
// 3. Text areas for a nested hasMany association (screenshots)
<fieldset>
<legend>Screenshots</legend>
<cfloop from="1" to="#ArrayLen(site.screenshots)#" index="i">
#fileField(objectName="site", association="screenshots", position=i, property="file", label="File ##i#")#
#textArea(objectName="site", association="screenshots", position=i, property="caption", label="Caption ##i#")#
</cfloop>
</fieldset>
// 4. Text area with label placement after and appended helper text
#textArea(objectName="user", property="bio", label="Bio", labelPlacement="before", append="<small>Max 500 characters</small>")#
Builds and returns a string containing a text area form control based on the supplied name.
Note: Pass any additional arguments like class, rel, and id, and the generated tag will also include those values as HTML attributes.
Name
Type
Required
Default
Description
name
string
Yes
Name to populate in tag's name attribute.
content
string
No
Content to display in textarea on page load.
label
string
No
The label text to use in the form control.
labelPlacement
string
No
around
Whether to place the label before, after, or wrapped around the form control. Label text placement can be controlled using aroundLeft or aroundRight.
prepend
string
No
String to prepend to the form control. Useful to wrap the form control with HTML tags.
append
string
No
String to append to the form control. Useful to wrap the form control with HTML tags.
prependToLabel
string
No
String to prepend to the form control's label. Useful to wrap the form control with HTML tags.
appendToLabel
string
No
String to append to the form control's label. Useful to wrap the form control with HTML tags.
encode
any
No
true
Use this argument to decide whether the output of the function should be encoded in order to prevent Cross Site Scripting (XSS) attacks. Set it to true to encode all relevant output for the specific HTML element in question (e.g. tag content, attribute values, and URLs). For HTML elements that have both tag content and attribute values you can set this argument to attributes to only encode attribute values and not tag content.
// 1. Basic textarea with a label and pre-populated content from params
#textAreaTag(name="description", label="Description", content=params.description)#
// 2. Textarea without a label, adding extra HTML attributes via additional arguments
#textAreaTag(name="bio", rows="6", cols="40")#
// 3. Textarea with label placement controlled and content wrapped with HTML using prepend/append
#textAreaTag(name="notes", label="Notes", labelPlacement="before", prepend="<div class=""field"">", append="</div>")#
Builds and returns a string containing a text field form control based on the supplied objectName and property.
Note: Pass any additional arguments like class, rel, and id, and the generated tag will also include those values as HTML attributes.
Name
Type
Required
Default
Description
objectName
any
Yes
The variable name of the object to build the form control for.
property
string
Yes
The name of the property to use in the form control.
association
string
No
The name of the association that the property is located on. Used for building nested forms that work with nested properties. If you are building a form with deep nesting, simply pass in a list to the nested object, and Wheels will figure it out.
position
string
No
The position used when referencing a hasMany relationship in the association argument. Used for building nested forms that work with nested properties. If you are building a form with deep nestings, simply pass in a list of positions, and Wheels will figure it out.
label
string
No
useDefaultLabel
The label text to use in the form control.
labelPlacement
string
No
around
Whether to place the label before, after, or wrapped around the form control. Label text placement can be controlled using aroundLeft or aroundRight.
prepend
string
No
String to prepend to the form control. Useful to wrap the form control with HTML tags.
append
string
No
String to append to the form control. Useful to wrap the form control with HTML tags.
prependToLabel
string
No
String to prepend to the form control's label. Useful to wrap the form control with HTML tags.
appendToLabel
string
No
String to append to the form control's label. Useful to wrap the form control with HTML tags.
errorElement
string
No
span
HTML tag to wrap the form control with when the object contains errors.
errorClass
string
No
field-with-errors
The class name of the HTML tag that wraps the form control when there are errors.
type
string
No
text
Input type attribute. Common examples in HTML5 and later are text (default), email, tel, and url.
encode
any
No
true
Use this argument to decide whether the output of the function should be encoded in order to prevent Cross Site Scripting (XSS) attacks. Set it to true to encode all relevant output for the specific HTML element in question (e.g. tag content, attribute values, and URLs). For HTML elements that have both tag content and attribute values you can set this argument to attributes to only encode attribute values and not tag content.
// 1. Basic text field bound to an object property
#textField(objectName="user", property="firstName", label="First Name")#
// 2. Use an HTML5 input type (email, tel, url, etc.) via the `type` argument
#textField(objectName="user", property="email", label="Email Address", type="email")#
// 3. Render fields for a hasMany association using `association` and `position`
<cfloop from="1" to="#ArrayLen(contact.phoneNumbers)#" index="i">
#textField(objectName="contact", association="phoneNumbers", position=i, property="phoneNumber", label="Phone ##i#")#
</cfloop>
// 4. Wrap the field with extra markup using `prepend` and `append`
#textField(objectName="user", property="username", label="Username", prepend="<div class=""field"">", append="</div>")#
Builds and returns a string containing a text field form control based on the supplied name.
Note: Pass any additional arguments like class, rel, and id, and the generated tag will also include those values as HTML attributes.
Name
Type
Required
Default
Description
name
string
Yes
Name to populate in tag's name attribute.
value
string
No
Value to populate in tag's value attribute.
label
string
No
The label text to use in the form control.
labelPlacement
string
No
around
Whether to place the label before, after, or wrapped around the form control. Label text placement can be controlled using aroundLeft or aroundRight.
prepend
string
No
String to prepend to the form control. Useful to wrap the form control with HTML tags.
append
string
No
String to append to the form control. Useful to wrap the form control with HTML tags.
prependToLabel
string
No
String to prepend to the form control's label. Useful to wrap the form control with HTML tags.
appendToLabel
string
No
String to append to the form control's label. Useful to wrap the form control with HTML tags.
type
string
No
text
Input type attribute. Common examples in HTML5 and later are text (default), email, tel, and url.
encode
any
No
true
Use this argument to decide whether the output of the function should be encoded in order to prevent Cross Site Scripting (XSS) attacks. Set it to true to encode all relevant output for the specific HTML element in question (e.g. tag content, attribute values, and URLs). For HTML elements that have both tag content and attribute values you can set this argument to attributes to only encode attribute values and not tag content.
// 1. Basic search field with a label and a pre-filled value from params
#textFieldTag(name="q", label="Search", value=params.q)#
// 2. Email input using the HTML5 type attribute
#textFieldTag(name="email", label="Email address", type="email", value=params.email)#
// 3. Field with label placed before the input and extra HTML attributes
#textFieldTag(name="username", label="Username", labelPlacement="before", class="form-control", placeholder="Enter username")#
// 4. Wrapping the input with Bootstrap input-group markup using prepend and append
#textFieldTag(name="website", label="Website", type="url", prepend="<div class=""input-group"">", append="</div>")#
// 1. Add a single time column to a new table
t = createTable(name='schedules');
t.string(columnNames='title', limit=255, allowNull=false);
t.time(columnNames='startTime', allowNull=false);
t.timestamps();
t.create();
// 2. Add multiple time columns at once
t = createTable(name='shifts');
t.string(columnNames='employeeName', limit=100, allowNull=false);
t.time(columnNames='clockIn,clockOut', allowNull=false);
t.timestamps();
t.create();
// 3. Add a nullable time column with a default to an existing table
t = changeTable(name='stores');
t.time(columnNames='openTime', allowNull=true, default='09:00:00');
t.change();
Returns a string describing the approximate time difference between the date passed in and the current date.
Name
Type
Required
Default
Description
fromTime
date
Yes
Date to compare from.
includeSeconds
boolean
No
false
Whether or not to include the number of seconds in the returned string.
toTime
date
No
[runtime expression]
Date to compare to.
// 1. Show how long ago a date was (relative to now)
aWhileAgo = DateAdd("d", -90, Now());
// Returns something like "3 months"
writeOutput(timeAgoInWords(aWhileAgo));
// 2. Include seconds for a very recent timestamp
justNow = DateAdd("s", -8, Now());
// Returns "less than 10 seconds"
writeOutput(timeAgoInWords(fromTime=justNow, includeSeconds=true));
// 3. Compare against a specific reference point instead of now
postDate = CreateDateTime(2024, 1, 15, 9, 0, 0);
referenceDate = CreateDateTime(2024, 3, 20, 9, 0, 0);
// Returns "about 2 months"
writeOutput(timeAgoInWords(fromTime=postDate, toTime=referenceDate));
Builds and returns a string containing three select form controls for hour, minute, and second based on the supplied objectName and property.
Name
Type
Required
Default
Description
objectName
any
No
The variable name of the object to build the form control for.
property
string
No
The name of the property to use in the form control.
association
string
No
The name of the association that the property is located on. Used for building nested forms that work with nested properties. If you are building a form with deep nesting, simply pass in a list to the nested object, and Wheels will figure it out.
position
string
No
The position used when referencing a hasMany relationship in the association argument. Used for building nested forms that work with nested properties. If you are building a form with deep nestings, simply pass in a list of positions, and Wheels will figure it out.
order
string
No
hour,minute,second
Use to change the order of or exclude time select tags.
separator
string
No
:
Use to change the character that is displayed between the time select tags.
minuteStep
numeric
No
1
Pass in 10 to only show minute 10, 20, 30, etc.
secondStep
numeric
No
1
Pass in 10 to only show seconds 10, 20, 30, etc.
includeBlank
any
No
false
Whether to include a blank option in the select form control. Pass true to include a blank line or a string that should represent what display text should appear for the empty value (for example, "- Select One -").
label
string
No
false
The label text to use in the form control.
labelPlacement
string
No
around
Whether to place the label before, after, or wrapped around the form control. Label text placement can be controlled using aroundLeft or aroundRight.
prepend
string
No
String to prepend to the form control. Useful to wrap the form control with HTML tags.
append
string
No
String to append to the form control. Useful to wrap the form control with HTML tags.
prependToLabel
string
No
String to prepend to the form control's label. Useful to wrap the form control with HTML tags.
appendToLabel
string
No
String to append to the form control's label. Useful to wrap the form control with HTML tags.
errorElement
string
No
span
HTML tag to wrap the form control with when the object contains errors.
errorClass
string
No
field-with-errors
The class name of the HTML tag that wraps the form control when there are errors.
combine
boolean
No
Set to false to not combine the select parts into a single DateTime object.
twelveHour
boolean
No
false
whether to display the hours in 24 or 12 hour format. 12 hour format has AM/PM drop downs
encode
any
No
true
Use this argument to decide whether the output of the function should be encoded in order to prevent Cross Site Scripting (XSS) attacks. Set it to true to encode all relevant output for the specific HTML element in question (e.g. tag content, attribute values, and URLs). For HTML elements that have both tag content and attribute values you can set this argument to attributes to only encode attribute values and not tag content.
<!--- Basic time select bound to a model object (hour, minute, second) --->
#timeSelect(objectName="business", property="openUntil")#
<!--- Show fields for hour and minute only --->
#timeSelect(objectName="business", property="openUntil", order="hour,minute")#
<!--- Only show 15-minute intervals --->
#timeSelect(objectName="appointment", property="dateTimeStart", minuteStep=15)#
<!--- Display in 12-hour format with AM/PM --->
#timeSelect(objectName="appointment", property="dateTimeStart", twelveHour=true)#
<!--- Include a blank option and use a custom separator --->
#timeSelect(objectName="shift", property="startTime", includeBlank="- Select -", separator=" | ")#
Builds and returns a string containing three select form controls for hour, minute, and second based on name.
Name
Type
Required
Default
Description
name
string
Yes
Name to populate in tag's name attribute.
selected
string
No
Value of option that should be selected by default.
order
string
No
hour,minute,second
Use to change the order of or exclude time select tags.
separator
string
No
:
Use to change the character that is displayed between the time select tags.
minuteStep
numeric
No
1
Pass in 10 to only show minute 10, 20, 30, etc.
secondStep
numeric
No
1
Pass in 10 to only show seconds 10, 20, 30, etc.
includeBlank
any
No
false
Whether to include a blank option in the select form control. Pass true to include a blank line or a string that should represent what display text should appear for the empty value (for example, "- Select One -").
label
string
No
The label text to use in the form control.
labelPlacement
string
No
around
Whether to place the label before, after, or wrapped around the form control. Label text placement can be controlled using aroundLeft or aroundRight.
prepend
string
No
String to prepend to the form control. Useful to wrap the form control with HTML tags.
append
string
No
String to append to the form control. Useful to wrap the form control with HTML tags.
prependToLabel
string
No
String to prepend to the form control's label. Useful to wrap the form control with HTML tags.
appendToLabel
string
No
String to append to the form control's label. Useful to wrap the form control with HTML tags.
combine
boolean
No
Set to false to not combine the select parts into a single DateTime object.
twelveHour
boolean
No
false
whether to display the hours in 24 or 12 hour format. 12 hour format has AM/PM drop downs
encode
any
No
true
Use this argument to decide whether the output of the function should be encoded in order to prevent Cross Site Scripting (XSS) attacks. Set it to true to encode all relevant output for the specific HTML element in question (e.g. tag content, attribute values, and URLs). For HTML elements that have both tag content and attribute values you can set this argument to attributes to only encode attribute values and not tag content.
// 1. Basic usage - render hour, minute, and second selects unbound from a model object
#timeSelectTags(name="timeOfMeeting", selected=params.timeOfMeeting)#
// 2. Show only hour and minute fields
#timeSelectTags(name="timeOfMeeting", selected=params.timeOfMeeting, order="hour,minute")#
// 3. Use 12-hour format with AM/PM and jump minutes in 15-minute increments
#timeSelectTags(name="appointmentTime", selected=params.appointmentTime, twelveHour=true, minuteStep=15, order="hour,minute")#
// 4. Include a blank option and add a label
#timeSelectTags(name="startTime", selected=params.startTime, includeBlank=true, label="Start Time")#
// 1. Add a single timestamp column to a new table
t = createTable(name='sessions');
t.string(columnNames='token', limit=64, allowNull=false);
t.timestamp(columnNames='expiresAt', allowNull=false);
t.timestamps();
t.create();
// 2. Add multiple timestamp columns at once
t = createTable(name='events');
t.string(columnNames='name', limit=255, allowNull=false);
t.timestamp(columnNames='startsAt,endsAt', allowNull=false);
t.timestamps();
t.create();
// 3. Add a nullable timestamp column with a default to an existing table
t = changeTable(name='articles');
t.timestamp(columnNames='publishedAt', allowNull=true, default='NOW()');
t.change();
// 4. Override the underlying column type (e.g. use 'timestamp' instead of the default 'datetime')
t = createTable(name='logs');
t.string(columnNames='message', limit=255, allowNull=false);
t.timestamp(columnNames='occurredAt', columnType='timestamp', allowNull=false);
t.timestamps();
t.create();
adds Wheels convention automatic timestamp and soft delete columns to table definition
// 1. Add Wheels convention timestamp and soft-delete columns to a new table
// Adds createdAt, updatedAt, and deletedAt (all nullable datetime columns)
t = createTable(name='articles');
t.string(columnNames='title', limit=255, allowNull=false);
t.text(columnNames='body');
t.timestamps();
t.create();
// 2. Use timestamps() alongside other column definitions in a full migration
t = createTable(name='posts');
t.string(columnNames='title', limit=255, allowNull=false);
t.string(columnNames='slug', limit=255, allowNull=false);
t.text(columnNames='body');
t.boolean(columnNames='published', default=false, allowNull=false);
t.references(columnNames='author');
t.timestamps();
t.create();
Capitalizes all words in the text to create a nicer looking title.
Name
Type
Required
Default
Description
word
string
Yes
The text to turn into a title.
// 1. Capitalize each word in a plain sentence
result = titleize("the quick brown fox");
// result -> "The Quick Brown Fox"
// 2. Capitalize a page title that is already mixed case
result = titleize("CFWheels is a framework for ColdFusion");
// result -> "CFWheels Is A Framework For ColdFusion"
// 3. Use titleize to format a record name for display
article = model("Article").findByKey(params.key);
writeOutput(titleize(article.title));
Assigns to the property specified the opposite of the property's current boolean value.
Throws an error if the property cannot be converted to a boolean value.
Returns this object if save called internally is false.
Name
Type
Required
Default
Description
property
string
Yes
save
boolean
No
true
Argument to decide whether save the property after it has been toggled.
// 1. Toggle a boolean property and save it immediately (default behavior)
user = model("User").findByKey(58);
// Returns true if the record was saved successfully, false otherwise
isSuccess = user.toggle("isActive");
// 2. Toggle a boolean property without saving to the database
user = model("User").findByKey(58);
user.toggle(property="isActive", save=false);
// user.isActive is now flipped in memory; call user.save() later to persist
// 3. Use the dynamic toggle helper generated by Wheels
user = model("User").findByKey(58);
isSuccess = user.toggleIsActive();
Truncates text to the specified length and replaces the last characters with the specified truncate string (which defaults to "...").
Name
Type
Required
Default
Description
text
string
Yes
The text to truncate.
length
numeric
No
30
Length to truncate the text to.
truncateString
string
No
...
String to replace the last characters with.
// 1. Truncate to a specific character length (defaults to "..." suffix)
truncated = truncate(text="CFWheels is a framework for ColdFusion", length=20);
// truncated -> "CFWheels is a fra..."
// 2. Use a custom truncate string instead of the default ellipsis
truncated = truncate(text="CFWheels is a framework for ColdFusion", truncateString=" (more)");
// truncated -> "CFWheels is a framework fo (more)"
// 3. Text shorter than the length limit is returned unchanged
truncated = truncate(text="Hello", length=30);
// truncated -> "Hello"
// 1. Add a single UUID column with the default newid() value
t = createTable(name='sessions');
t.uniqueidentifier(columnNames='token');
t.timestamps();
t.create();
// 2. Add a UUID column that allows NULL values
t = createTable(name='invitations');
t.string(columnNames='email', limit=255, allowNull=false);
t.uniqueidentifier(columnNames='inviteToken', allowNull=true);
t.timestamps();
t.create();
// 3. Alter an existing table to add multiple UUID columns at once
t = changeTable(name='documents');
t.uniqueidentifier(columnNames='publicId,shareToken', allowNull=false);
t.change();
Migrates up: will be executed when migrating your schema forward
Along with down(), these are the two main functions in any migration file
Only available in a migration CFC
// 1. Create a new table (typical migration forward)
function up() {
var state = {};
transaction {
try {
t = createTable(name="posts");
t.string(columnNames="title", limit=255);
t.text(columnNames="body");
t.boolean(columnNames="published", default=0);
t.timestamps();
t.create();
} catch (any e) {
state.exception = e;
}
if (structKeyExists(state, "exception")) {
transaction action="rollback";
throw(
errorCode = "1",
detail = state.exception.detail,
message = state.exception.message,
type = "any"
);
} else {
transaction action="commit";
}
}
}
// 2. Add a column to an existing table
function up() {
var state = {};
transaction {
try {
addColumn(table="users", columnType="string", columnName="avatarUrl", limit=500, allowNull=true);
} catch (any e) {
state.exception = e;
}
if (structKeyExists(state, "exception")) {
transaction action="rollback";
throw(
errorCode = "1",
detail = state.exception.detail,
message = state.exception.message,
type = "any"
);
} else {
transaction action="commit";
}
}
}
// 3. Run raw SQL and seed initial data in the same migration
function up() {
var state = {};
transaction {
try {
execute("ALTER TABLE products ADD COLUMN sku VARCHAR(50)");
addRecord(table="settings", key="maintenance_mode", value="false");
} catch (any e) {
state.exception = e;
}
if (structKeyExists(state, "exception")) {
transaction action="rollback";
throw(
errorCode = "1",
detail = state.exception.detail,
message = state.exception.message,
type = "any"
);
} else {
transaction action="commit";
}
}
}
Updates the object with the supplied properties and saves it to the database.
Returns true if the object was saved successfully to the database and false otherwise.
Name
Type
Required
Default
Description
properties
struct
No
[runtime expression]
The properties you want to set on the object (can also be passed in as named arguments).
parameterize
any
No
true
Set to true to use cfqueryparam on all columns, or pass in a list of property names to use cfqueryparam on those only.
reload
boolean
No
false
Set to true to force Wheels to query the database even though an identical query for this model may have been run in the same request. (The default in Wheels is to get the second query from the model's request-level cache.)
validate
boolean
No
true
Set to false to skip validations for this operation.
transaction
string
No
[runtime expression]
Set this to commit to update the database, rollback to run all the database queries but not commit them, or none to skip transaction handling altogether.
callbacks
boolean
No
true
Set to false to disable callbacks for this method.
allowExplicitTimestamps
boolean
No
false
Set this to true to allow explicit assignment of createdAt or updatedAt properties
// 1. Find a post and update its title
post = model("Post").findByKey(33);
post.update(title="New version of Wheels just released");
// 2. Update multiple properties from form/URL params on an existing object
post = model("Post").findByKey(params.key);
isSuccess = post.update(title="New version of Wheels just released", properties=params.post);
// 3. Skip validations when updating (useful for admin operations)
post = model("Post").findByKey(params.key);
isSuccess = post.update(properties=params.post, validate=false);
// 4. Scoped call via a hasOne association (setBio calls bio.update(authorId=author.id) internally)
author = model("Author").findByKey(params.authorId);
bio = model("Bio").findByKey(params.bioId);
author.setBio(bio);
// 5. Scoped call via a hasMany association (addCar calls car.update(ownerId=owner.id) internally)
anOwner = model("Owner").findByKey(params.ownerId);
aCar = model("Car").findByKey(params.carId);
anOwner.addCar(aCar);
// 6. Scoped call to disassociate a record (removeComment calls comment.update(postId="") internally)
aPost = model("Post").findByKey(params.postId);
aComment = model("Comment").findByKey(params.commentId);
aPost.removeComment(aComment);
Updates all properties for the records that match the where argument.
Property names and values can be passed in either using named arguments or as a struct to the properties argument.
By default, objects will not be instantiated and therefore callbacks and validations are not invoked.
You can change this behavior by passing in instantiate=true.
This method returns the number of records that were updated.
Name
Type
Required
Default
Description
where
string
No
Maps to the WHERE clause of the query (or HAVING when necessary). The following operators are supported: =, !=, <>, <, <=, >, >=, LIKE, NOT LIKE, IN, NOT IN, IS NULL, IS NOT NULL, AND, and OR (note that the key words need to be written in upper case). You can also use parentheses to group statements. Nested queries not allowed. You do not need to specify the table name(s); Wheels will do that for you.
include
string
No
Associations that should be included in the query using INNER or LEFT OUTER joins (which join type that is used depends on how the association has been set up in your model). If all included associations are set on the current model, you can specify them in a list (e.g. department,addresses,emails). You can build more complex include strings by using parentheses when the association is set on an included model, like album(artist(genre)), for example. These complex include strings only work when returnAs is set to query though.
properties
struct
No
[runtime expression]
The properties you want to set on the object (can also be passed in as named arguments).
reload
boolean
No
false
Set to true to force Wheels to query the database even though an identical query for this model may have been run in the same request. (The default in Wheels is to get the second query from the model's request-level cache.)
parameterize
any
No
true
Set to true to use cfqueryparam on all columns, or pass in a list of property names to use cfqueryparam on those only.
instantiate
boolean
No
false
Whether or not to instantiate the object(s) first. When objects are not instantiated, any callbacks and validations set on them will be skipped.
useIndex
struct
No
[runtime expression]
If you want to specify table index hints, pass in a structure of index names using your model names as the structure keys. Eg: {user="idx_users", post="idx_posts"}. This feature is only supported by MySQL and SQL Server.
validate
boolean
No
true
Set to false to skip validations for this operation.
transaction
string
No
[runtime expression]
Set this to commit to update the database, rollback to run all the database queries but not commit them, or none to skip transaction handling altogether.
callbacks
boolean
No
true
Set to false to disable callbacks for this method.
includeSoftDeletes
boolean
No
false
Set to true to include soft-deleted records in the queries that this method runs.
// 1. Update a single property on all matching records (returns count of updated rows)
recordsUpdated = model("post").updateAll(published=1, where="published=0");
// 2. Update multiple properties on matching records using named arguments
recordsUpdated = model("post").updateAll(
published=1,
publishedAt=Now(),
where="published=0"
);
// 3. Update using a properties struct instead of named arguments
props = {status="archived", updatedBy="system"};
recordsUpdated = model("post").updateAll(properties=props, where="createdAt < '#DateAdd("yyyy", -2, Now())#'");
// 4. Instantiate each matching object so that callbacks and validations run
recordsUpdated = model("user").updateAll(active=0, where="lastLoginAt < '#DateAdd("d", -365, Now())#'", instantiate=true);
// 5. Scoped call via a hasMany association — equivalent to
// model("comment").updateAll(postId="", where="postId=#post.id#")
post = model("post").findByKey(params.postId);
post.removeAllComments();
Finds the object with the supplied key and saves it (if validation permits it) with the supplied properties and / or named arguments.
Property names and values can be passed in either using named arguments or as a struct to the properties argument.
Returns true if the object was found and updated successfully, false otherwise.
Name
Type
Required
Default
Description
key
any
Yes
Primary key value(s) of the record to fetch. Separate with comma if passing in multiple primary key values. Accepts a string, list, or a numeric value.
properties
struct
No
[runtime expression]
The properties you want to set on the object (can also be passed in as named arguments).
reload
boolean
No
false
Set to true to force Wheels to query the database even though an identical query for this model may have been run in the same request. (The default in Wheels is to get the second query from the model's request-level cache.)
validate
boolean
No
true
Set to false to skip validations for this operation.
transaction
string
No
[runtime expression]
Set this to commit to update the database, rollback to run all the database queries but not commit them, or none to skip transaction handling altogether.
callbacks
boolean
No
true
Set to false to disable callbacks for this method.
includeSoftDeletes
boolean
No
false
Set to true to include soft-deleted records in the queries that this method runs.
// 1. Update a post using a positional key and a params struct
result = model("post").updateByKey(33, params.post);
// 2. Update a post using named arguments
result = model("post").updateByKey(key=33, title="New version of Wheels just released", published=1);
// 3. Skip validations when updating a record (useful for admin operations)
result = model("post").updateByKey(key=33, validate=false, status="archived");
// 4. Include soft-deleted records when looking up the key to update
result = model("post").updateByKey(key=33, includeSoftDeletes=true, restoredAt=Now());
Gets an object based on the arguments used and updates it with the supplied properties.
Returns true if an object was found and updated successfully, false otherwise.
Name
Type
Required
Default
Description
where
string
No
Maps to the WHERE clause of the query (or HAVING when necessary). The following operators are supported: =, !=, <>, <, <=, >, >=, LIKE, NOT LIKE, IN, NOT IN, IS NULL, IS NOT NULL, AND, and OR (note that the key words need to be written in upper case). You can also use parentheses to group statements. Nested queries not allowed. You do not need to specify the table name(s); Wheels will do that for you.
order
string
No
Maps to the ORDER BY clause of the query. You do not need to specify the table name(s); Wheels will do that for you.
properties
struct
No
[runtime expression]
The properties you want to set on the object (can also be passed in as named arguments).
reload
boolean
No
false
Set to true to force Wheels to query the database even though an identical query for this model may have been run in the same request. (The default in Wheels is to get the second query from the model's request-level cache.)
validate
boolean
No
true
Set to false to skip validations for this operation.
useIndex
struct
No
[runtime expression]
If you want to specify table index hints, pass in a structure of index names using your model names as the structure keys. Eg: {user="idx_users", post="idx_posts"}. This feature is only supported by MySQL and SQL Server.
transaction
string
No
[runtime expression]
Set this to commit to update the database, rollback to run all the database queries but not commit them, or none to skip transaction handling altogether.
callbacks
boolean
No
true
Set to false to disable callbacks for this method.
includeSoftDeletes
boolean
No
false
// 1. Update the most recently released product by setting its `featured` flag
result = model("Product").updateOne(order="releaseDate DESC", featured=1);
// 2. Update a specific record matching a `where` clause
result = model("Order").updateOne(where="status='pending' AND createdAt < '#DateAdd('d', -7, Now())#'", status="expired");
// 3. Skip validations when updating, e.g. to force a status change
result = model("Article").updateOne(where="status='draft'", order="createdAt ASC", status="published", validate=false);
// 4. Scoped call via a `hasOne` association (calls `updateOne` internally)
// Given `hasOne(name="profile")` on the User model:
aUser = model("User").findByKey(params.userId);
aUser.removeProfile();
Updates a single property and saves the record without going through the normal validation procedure.
This is especially useful for boolean flags on existing records.
Name
Type
Required
Default
Description
property
string
No
Name of the property to update the value for globally.
value
any
No
Value to set on the given property globally.
parameterize
any
No
true
Set to true to use cfqueryparam on all columns, or pass in a list of property names to use cfqueryparam on those only.
transaction
string
No
[runtime expression]
Set this to commit to update the database, rollback to run all the database queries but not commit them, or none to skip transaction handling altogether.
callbacks
boolean
No
true
Set to false to disable callbacks for this method.
// 1. Set a boolean flag on an existing record (the primary use case)
product = model("Product").findByKey(56);
product.updateProperty("new", 1);
// 2. Mark a user account as active without triggering validations
user = model("User").findByKey(params.userId);
user.updateProperty("active", true);
// 3. Update a property and skip callbacks
post = model("Post").findByKey(params.id);
post.updateProperty(property="featured", value=true, callbacks=false);
Updates an existing record in a table
Only available in a migration CFC
Name
Type
Required
Default
Description
table
string
Yes
The table name where the record is
where
string
No
The where clause, i.e admin = 1
// 1. Update a single column for all rows in a table
updateRecord(
table = "settings",
value = "My Updated App"
);
// 2. Update specific columns using a where clause to target matching rows
updateRecord(
table = "users",
where = "role = 'guest'",
active = false
);
// 3. Update multiple columns during a migration's up() function
component extends="wheels.migrator.Migration" {
function up() {
updateRecord(
table = "users",
where = "id = 1",
firstName = "Bruce",
lastName = "Wayne",
email = "bruce@wayneenterprises.com"
);
}
function down() {
updateRecord(
table = "users",
where = "id = 1",
firstName = "Clark",
lastName = "Kent",
email = "clark@dailyplanet.com"
);
}
}
Inserts or updates multiple records in a single batch operation (upsert).
Uses database-specific conflict resolution syntax (e.g., ON CONFLICT ... DO UPDATE for PostgreSQL/SQLite).
The uniqueBy argument specifies which properties form the unique constraint for conflict detection.
Name
Type
Required
Default
Description
records
array
Yes
Array of structs, each containing property name/value pairs.
uniqueBy
string
Yes
Comma-delimited list of property names that form the unique constraint for conflict detection.
timestamps
boolean
No
true
Set to false to skip automatic createdAt/updatedAt timestamping.
transaction
string
No
[runtime expression]
Set this to commit to update the database, rollback to run all the database queries but not commit them, or none to skip transaction handling altogether.
parameterize
any
No
true
Set to true to use cfqueryparam on all columns, or pass in a list of property names to use cfqueryparam on those only.
// 1. Upsert a batch of products using their SKU as the unique constraint
records = [
{sku: "WIDGET-001", name: "Widget Standard", price: 9.99, stock: 100},
{sku: "WIDGET-002", name: "Widget Deluxe", price: 19.99, stock: 50},
{sku: "GADGET-001", name: "Gadget Pro", price: 49.99, stock: 25}
];
result = model("Product").upsertAll(records=records, uniqueBy="sku");
// result -> {upsertedCount: 3}
// 2. Upsert with a composite unique constraint (e.g., userId + date for daily stats)
stats = [
{userId: 1, reportDate: "2024-06-01", pageViews: 42, clicks: 7},
{userId: 2, reportDate: "2024-06-01", pageViews: 18, clicks: 3}
];
result = model("DailyStat").upsertAll(records=stats, uniqueBy="userId,reportDate");
// result -> {upsertedCount: 2}
// 3. Upsert without automatic timestamps (e.g., when importing legacy data)
imports = [
{externalId: "EXT-100", title: "Legacy Record A", status: "active"},
{externalId: "EXT-101", title: "Legacy Record B", status: "archived"}
];
result = model("ImportedRecord").upsertAll(
records = imports,
uniqueBy = "externalId",
timestamps = false
);
// result -> {upsertedCount: 2}
Builds and returns a string containing a URL field form control based on the supplied objectName and property.
Note: Pass any additional arguments like class, rel, and id, and the generated tag will also include those values as HTML attributes.
Name
Type
Required
Default
Description
objectName
any
Yes
The variable name of the object to build the form control for.
property
string
Yes
The name of the property to use in the form control.
association
string
No
The name of the association that the property is located on. Used for building nested forms that work with nested properties. If you are building a form with deep nesting, simply pass in a list to the nested object, and Wheels will figure it out.
position
string
No
The position used when referencing a hasMany relationship in the association argument. Used for building nested forms that work with nested properties. If you are building a form with deep nestings, simply pass in a list of positions, and Wheels will figure it out.
label
string
No
useDefaultLabel
The label text to use in the form control.
labelPlacement
string
No
around
Whether to place the label before, after, or wrapped around the form control. Label text placement can be controlled using aroundLeft or aroundRight.
prepend
string
No
String to prepend to the form control. Useful to wrap the form control with HTML tags.
append
string
No
String to append to the form control. Useful to wrap the form control with HTML tags.
prependToLabel
string
No
String to prepend to the form control's label. Useful to wrap the form control with HTML tags.
appendToLabel
string
No
String to append to the form control's label. Useful to wrap the form control with HTML tags.
errorElement
string
No
span
HTML tag to wrap the form control with when the object contains errors.
errorClass
string
No
field-with-errors
The class name of the HTML tag that wraps the form control when there are errors.
encode
any
No
true
Use this argument to decide whether the output of the function should be encoded in order to prevent Cross Site Scripting (XSS) attacks. Set it to true to encode all relevant output for the specific HTML element in question (e.g. tag content, attribute values, and URLs). For HTML elements that have both tag content and attribute values you can set this argument to attributes to only encode attribute values and not tag content.
// 1. Basic URL field bound to an object property
#urlField(objectName="profile", property="websiteUrl")#
// 2. URL field with a custom label and a CSS class
#urlField(label="Website URL", objectName="profile", property="websiteUrl", class="form-control")#
// 3. Nested URL field for a contacts association (hasMany)
<cfloop from="1" to="#ArrayLen(company.contacts)#" index="i">
#urlField(label="Contact Website ##i#", objectName="company", association="contacts", position=i, property="websiteUrl")#
</cfloop>
Builds and returns a string containing a URL field form control based on the supplied name.
Note: Pass any additional arguments like class, rel, and id, and the generated tag will also include those values as HTML attributes.
Name
Type
Required
Default
Description
name
string
Yes
Name to populate in tag's name attribute.
value
string
No
Value to populate in tag's value attribute.
label
string
No
The label text to use in the form control.
labelPlacement
string
No
around
Whether to place the label before, after, or wrapped around the form control. Label text placement can be controlled using aroundLeft or aroundRight.
prepend
string
No
String to prepend to the form control. Useful to wrap the form control with HTML tags.
append
string
No
String to append to the form control. Useful to wrap the form control with HTML tags.
prependToLabel
string
No
String to prepend to the form control's label. Useful to wrap the form control with HTML tags.
appendToLabel
string
No
String to append to the form control's label. Useful to wrap the form control with HTML tags.
encode
any
No
true
Use this argument to decide whether the output of the function should be encoded in order to prevent Cross Site Scripting (XSS) attacks. Set it to true to encode all relevant output for the specific HTML element in question (e.g. tag content, attribute values, and URLs). For HTML elements that have both tag content and attribute values you can set this argument to attributes to only encode attribute values and not tag content.
// 1. Basic URL field with a label
#urlFieldTag(name="website", label="Website")#
// 2. Pre-filled with a value from params and a placeholder attribute
#urlFieldTag(name="homepage", label="Homepage", value=params.homepage, placeholder="https://example.com")#
// 3. Label placed before the field with an extra CSS class
#urlFieldTag(name="profileUrl", label="Profile URL", labelPlacement="before", class="form-control")#
// 4. Field wrapped with markup using prepend and append
#urlFieldTag(name="website", label="Website", prepend="<div class=""input-group"">", append="</div>")#
Creates an internal URL based on supplied arguments.
Name
Type
Required
Default
Description
route
string
No
Name of a route that you have configured in config/routes.cfm.
controller
string
No
Name of the controller to include in the URL.
action
string
No
Name of the action to include in the URL.
key
any
No
Key(s) to include in the URL.
params
string
No
Any additional parameters to be set in the query string (example: wheels=cool&x=y). Please note that Wheels uses the & and = characters to split the parameters and encode them properly for you. However, if you need to pass in & or = as part of the value, then you need to encode them (and only them), example: a=cats%26dogs%3Dtrouble!&b=1.
anchor
string
No
Sets an anchor name to be appended to the path.
onlyPath
boolean
No
true
If true, returns only the relative URL (no protocol, host name or port).
host
string
No
Set this to override the current host.
protocol
string
No
Set this to override the current protocol.
port
numeric
No
0
Set this to override the current port number.
encode
boolean
No
true
Encode URL parameters using EncodeForURL(). Please note that this does not make the string safe for placement in HTML attributes, for that you need to wrap the result in EncodeForHtmlAttribute() or use linkTo(), startFormTag() etc instead.
// 1. Create the URL for the `logOut` action on the `account` controller, typically resulting in `/account/log-out`
urlFor(controller="account", action="logOut")
// 2. Create a URL with an anchor appended to it
urlFor(action="comments", anchor="comment10")
// 3. Create a URL based on a named route that expects `categorySlug` and `productSlug` params
urlFor(route="product", categorySlug="accessories", productSlug="battery-charger")
// 4. Generate an absolute URL (including protocol and host) to use in an email or external link
urlFor(controller="account", action="confirm", key=user.key(), onlyPath=false, protocol="https")
// 5. Append extra query string params not covered by the route pattern
urlFor(controller="products", action="index", params="sort=price&dir=asc")
Used within a controller's config() function to specify controller- or action-specific layouts.
Name
Type
Required
Default
Description
template
string
Yes
Name of the layout template or function name you want to use.
ajax
string
No
Name of the layout template you want to use for AJAX requests.
except
string
No
List of actions that should not get the layout.
only
string
No
List of actions that should only get the layout.
useDefault
boolean
No
true
When specifying conditions or a function, pass in true to use the default layout.cfm if none of the conditions are met.
// 1. Use a custom layout for the entire controller, except for one action.
// Declared inside the controller's config() function.
usesLayout(template="myLayout", except="myAjax");
// 2. Apply a custom layout only to specific actions; all other actions
// use the default layout.cfm.
usesLayout(template="myLayout", only="termsOfService,shippingPolicy");
// 3. Serve a lightweight layout for AJAX requests while normal requests
// still receive the full layout.
usesLayout(template="myLayout", ajax="ajaxLayout");
// 4. Delegate layout selection to a private function. The function receives
// the current action name and should return the layout template name or
// true to fall back to the default layout.cfm.
usesLayout("chooseLayout");
// Example chooseLayout() function in the same controller:
// private function chooseLayout(action) {
// if (action == "print") return "printLayout";
// return true; // fall back to default layout.cfm
// }
// 5. Use a function-based layout but fall back to layout.cfm when the
// function returns nothing (useDefault defaults to true).
usesLayout(template="chooseLayout", useDefault=true);
Runs the validation on the object and returns true if it passes it.
Wheels will run the validation process automatically whenever an object is saved to the database, but sometimes it's useful to be able to run this method to see if the object is valid without saving it to the database.
Name
Type
Required
Default
Description
callbacks
boolean
No
true
Set to false to disable callbacks for this method.
validateAssociations
boolean
No
false
// 1. Check if a new user object passes validation before proceeding
user = model("User").new(params.user);
if (user.valid()) {
// object passed all validations, safe to proceed
redirectTo(action="dashboard");
} else {
renderView(action="new");
}
// 2. Validate without running before/after validation callbacks
user = model("User").new(params.user);
if (user.valid(callbacks=false)) {
user.save(callbacks=false);
}
// 3. Validate the object and any associated (nested) objects together
order = model("Order").new(params.order);
if (order.valid(validateAssociations=true)) {
order.save();
} else {
// errors may include issues from associated line items
writeOutput(order.errorsAsHTML());
}
Registers method(s) that should be called to validate objects before they are saved.
Name
Type
Required
Default
Description
methods
string
No
Method name or list of method names to call. Can also be called with the method argument.
condition
string
No
String expression to be evaluated that decides if validation will be run (if the expression returns true validation will run).
unless
string
No
String expression to be evaluated that decides if validation will be run (if the expression returns false validation will run).
when
string
No
onSave
Pass in onCreate or onUpdate to limit when this validation occurs (by default validation will occur on both create and update, i.e. onSave).
// 1. Register a custom validation method to run on every save
function config() {
// `checkPhoneNumber` will be called whenever an object is created or updated.
validate("checkPhoneNumber");
}
function checkPhoneNumber() {
// Make sure the area code is `614`.
return Left(this.phoneNumber, 3) == "614";
}
// 2. Register multiple custom validation methods at once
function config() {
validate(methods="checkPhoneNumber,checkEmailDomain");
}
// 3. Limit validation to create or update only
function config() {
// Only run `checkTrialExpiry` when updating an existing record.
validate(methods="checkTrialExpiry", when="onUpdate");
}
// 4. Run a custom validation only when a condition is met
function config() {
// `checkBillingAddress` is skipped when the order is free.
validate(methods="checkBillingAddress", condition="this.totalAmount gt 0");
}
// 5. Skip a custom validation when an `unless` expression is true
function config() {
// `checkCreditCard` is skipped for admin users.
validate(methods="checkCreditCard", unless="this.isAdmin");
}
Registers method(s) that should be called to validate new objects before they are inserted.
Name
Type
Required
Default
Description
methods
string
No
Method name or list of method names to call. Can also be called with the method argument.
condition
string
No
String expression to be evaluated that decides if validation will be run (if the expression returns true validation will run).
unless
string
No
String expression to be evaluated that decides if validation will be run (if the expression returns false validation will run).
// 1. Register a custom method to validate new objects before insert
function config() {
// `checkPhoneNumber` will only be called when creating a new record.
validateOnCreate("checkPhoneNumber");
}
function checkPhoneNumber() {
// Make sure area code is `614`.
return Left(this.phoneNumber, 3) == "614";
}
// 2. Register multiple validation methods at once
function config() {
validateOnCreate("checkPhoneNumber,checkReferralCode");
}
function checkPhoneNumber() {
return Left(this.phoneNumber, 3) == "614";
}
function checkReferralCode() {
if (Len(this.referralCode) && !isValidReferral(this.referralCode)) {
addError(property="referralCode", message="Invalid referral code.");
}
}
// 3. Only run the validation when a condition is met
function config() {
// Only validate the phone number on create when the user is in the US.
validateOnCreate(methods="checkPhoneNumber", condition="this.country eq 'US'");
}
function checkPhoneNumber() {
return IsNumeric(this.phoneNumber);
}
Registers method(s) that should be called to validate existing objects before they are updated.
Name
Type
Required
Default
Description
methods
string
No
Method name or list of method names to call. Can also be called with the method argument.
condition
string
No
String expression to be evaluated that decides if validation will be run (if the expression returns true validation will run).
unless
string
No
String expression to be evaluated that decides if validation will be run (if the expression returns false validation will run).
// 1. Register a single custom validation method to run only on updates
component extends="Model" {
function config() {
validateOnUpdate("checkPhoneNumber");
}
private boolean function checkPhoneNumber() {
// Make sure area code is 614
return Left(this.phoneNumber, 3) == "614";
}
}
// 2. Register multiple custom validation methods for updates
component extends="Model" {
function config() {
validateOnUpdate(methods="checkStatus,checkExpiry");
}
private boolean function checkStatus() {
return ListFindNoCase("active,pending,suspended", this.status);
}
private boolean function checkExpiry() {
return this.expiresAt > Now();
}
}
// 3. Only validate when a condition is met (run only for premium accounts)
component extends="Model" {
function config() {
validateOnUpdate(methods="checkBillingAddress", condition="this.accountType eq 'premium'");
}
private boolean function checkBillingAddress() {
return Len(Trim(this.billingAddress)) GT 0;
}
}
Validates that the value of the specified property also has an identical confirmation value.
This is common when having a user type in their email address a second time to confirm, confirming a password by typing it a second time, etc.
The confirmation value only exists temporarily and never gets saved to the database.
By convention, the confirmation property has to be named the same as the property with "Confirmation" appended at the end.
Using the password example, to confirm our password property, we would create a property called passwordConfirmation.
Name
Type
Required
Default
Description
properties
string
No
Name of property or list of property names to validate against (can also be called with the property argument).
message
string
No
[property] should match confirmation
Supply a custom error message here to override the built-in one.
when
string
No
onSave
Pass in onCreate or onUpdate to limit when this validation occurs (by default validation will occur on both create and update, i.e. onSave).
condition
string
No
String expression to be evaluated that decides if validation will be run (if the expression returns true validation will run).
unless
string
No
String expression to be evaluated that decides if validation will be run (if the expression returns false validation will run).
caseSensitive
boolean
No
false
Ensure the confirmed property comparison is case sensitive
// 1. Require a confirmed password when creating a new user account
// The form should include a `passwordConfirmation` field that the user types their password into a second time
validatesConfirmationOf(property="password", when="onCreate", message="Your password and its confirmation do not match. Please try again.");
// 2. Confirm an email address on every save (default `when="onSave"`)
// A matching `emailConfirmation` property must be set on the object before saving
validatesConfirmationOf(property="email");
// 3. Confirm multiple properties and use a case-sensitive comparison
// Both `passwordConfirmation` and `pinConfirmation` must match exactly (including letter case)
validatesConfirmationOf(properties="password,pin", caseSensitive=true);
Validates that the value of the specified property does not exist in the supplied list.
Name
Type
Required
Default
Description
properties
string
No
Name of property or list of property names to validate against (can also be called with the property argument).
list
string
Yes
Single value or list of values that should not be allowed.
message
string
No
[property] is reserved
Supply a custom error message here to override the built-in one.
when
string
No
onSave
Pass in onCreate or onUpdate to limit when this validation occurs (by default validation will occur on both create and update, i.e. onSave).
allowBlank
boolean
No
false
If set to true, validation will be skipped if the property value is an empty string or doesn't exist at all. This is useful if you only want to run this validation after it passes the validatesPresenceOf test, thus avoiding duplicate error messages if it doesn't.
condition
string
No
String expression to be evaluated that decides if validation will be run (if the expression returns true validation will run).
unless
string
No
String expression to be evaluated that decides if validation will be run (if the expression returns false validation will run).
// 1. Prevent reserved words from being saved as a programming language name
validatesExclusionOf(property="language", list="php,fortran", message="[property] is reserved. Try a real language.");
// 2. Validate multiple properties against the same exclusion list (e.g. reserved usernames)
validatesExclusionOf(properties="username,displayName", list="admin,root,superuser,moderator");
// 3. Only enforce the exclusion on create, and skip validation when the value is blank
validatesExclusionOf(property="referralCode", list="FREE,GRATIS,FREEBIE", when="onCreate", allowBlank=true);
// 4. Conditionally enforce the exclusion based on a model property
validatesExclusionOf(property="status", list="banned,suspended", condition="this.isAdmin");
Validates that the value of the specified property is formatted correctly by matching it against a regular expression using the regEx argument and / or against a built-in CFML validation type using the type argument (creditcard, date, email, etc.).
Name
Type
Required
Default
Description
properties
string
No
Name of property or list of property names to validate against (can also be called with the property argument).
regEx
string
No
Regular expression to verify against.
type
string
No
One of the following types to verify against: creditcard, date, email, eurodate, guid, social_security_number, ssn, telephone, time, URL, USdate, UUID, variableName, zipcode (will be passed through to your CFML engine's IsValid() function).
message
string
No
[property] is invalid
Supply a custom error message here to override the built-in one.
when
string
No
onSave
Pass in onCreate or onUpdate to limit when this validation occurs (by default validation will occur on both create and update, i.e. onSave).
allowBlank
boolean
No
false
If set to true, validation will be skipped if the property value is an empty string or doesn't exist at all. This is useful if you only want to run this validation after it passes the validatesPresenceOf test, thus avoiding duplicate error messages if it doesn't.
condition
string
No
String expression to be evaluated that decides if validation will be run (if the expression returns true validation will run).
unless
string
No
String expression to be evaluated that decides if validation will be run (if the expression returns false validation will run).
// 1. Validate that a credit card number is in the correct format
validatesFormatOf(property="creditCard", type="creditcard");
// 2. Validate an email address using a regular expression
validatesFormatOf(property="email", type="email");
// 3. Validate a US phone number with a custom regex and allow blank values
validatesFormatOf(
property="phone",
regEx="^\d{3}-\d{3}-\d{4}$",
allowBlank=true,
message="[property] must be in the format 555-867-5309."
);
// 4. Validate that an email ends with `.se` only when a condition is met and it's not Sunday
validatesFormatOf(
property="email",
regEx="^.*@.*\.se$",
condition="ipCheck()",
unless="DayOfWeek() eq 1",
message="Sorry, you must have a Swedish email address to use this website."
);
Validates that the value of the specified property exists in the supplied list.
Name
Type
Required
Default
Description
properties
string
No
Name of property or list of property names to validate against (can also be called with the property argument).
list
string
Yes
List of allowed values.
message
string
No
[property] is not included in the list
Supply a custom error message here to override the built-in one.
when
string
No
onSave
Pass in onCreate or onUpdate to limit when this validation occurs (by default validation will occur on both create and update, i.e. onSave).
allowBlank
boolean
No
false
If set to true, validation will be skipped if the property value is an empty string or doesn't exist at all. This is useful if you only want to run this validation after it passes the validatesPresenceOf test, thus avoiding duplicate error messages if it doesn't.
condition
string
No
String expression to be evaluated that decides if validation will be run (if the expression returns true validation will run).
unless
string
No
String expression to be evaluated that decides if validation will be run (if the expression returns false validation will run).
// 1. Validate that a user selects a valid framework choice
validatesInclusionOf(property="frameworkOfChoice", list="cfwheels,rails,django", message="Please select a supported framework.");
// 2. Validate a status field and skip validation if the value is blank
validatesInclusionOf(property="status", list="active,inactive,pending", allowBlank=true);
// 3. Validate a role only when creating a new record
validatesInclusionOf(property="role", list="admin,editor,viewer", when="onCreate");
// 4. Validate a priority field only when a condition is met
validatesInclusionOf(property="priority", list="low,medium,high", condition="this.isAssigned()");
Validates that the value of the specified property matches the length requirements supplied.
Use the exactly, maximum, minimum and within arguments to specify the length requirements.
Name
Type
Required
Default
Description
properties
string
No
Name of property or list of property names to validate against (can also be called with the property argument).
message
string
No
[property] is the wrong length
Supply a custom error message here to override the built-in one.
when
string
No
onSave
Pass in onCreate or onUpdate to limit when this validation occurs (by default validation will occur on both create and update, i.e. onSave).
allowBlank
boolean
No
false
If set to true, validation will be skipped if the property value is an empty string or doesn't exist at all. This is useful if you only want to run this validation after it passes the validatesPresenceOf test, thus avoiding duplicate error messages if it doesn't.
exactly
numeric
No
0
The exact length that the property value must be.
maximum
numeric
No
0
The maximum length that the property value can be.
minimum
numeric
No
0
The minimum length that the property value can be.
within
string
No
A list of two values (minimum and maximum) that the length of the property value must fall within.
condition
string
No
String expression to be evaluated that decides if validation will be run (if the expression returns true validation will run).
unless
string
No
String expression to be evaluated that decides if validation will be run (if the expression returns false validation will run).
// 1. Validate a maximum length on multiple properties, using [property] in the
// message so the property label is injected dynamically at runtime
validatesLengthOf(
properties="firstName,lastName",
maximum=50,
message="Please shorten your [property] (50 characters max)."
);
// 2. Validate that a password falls within a range of character lengths
validatesLengthOf(
property="password",
within="4,20",
message="The password must be between 4 and 20 characters."
);
// 3. Validate an exact length only on create, skipping blank values
validatesLengthOf(
property="postalCode",
exactly=5,
when="onCreate",
allowBlank=true,
message="Postal code must be exactly 5 characters."
);
Validates that the value of the specified property is numeric.
Name
Type
Required
Default
Description
properties
string
No
Name of property or list of property names to validate against (can also be called with the property argument).
message
string
No
[property] is not a number
Supply a custom error message here to override the built-in one.
when
string
No
onSave
Pass in onCreate or onUpdate to limit when this validation occurs (by default validation will occur on both create and update, i.e. onSave).
allowBlank
boolean
No
false
If set to true, validation will be skipped if the property value is an empty string or doesn't exist at all. This is useful if you only want to run this validation after it passes the validatesPresenceOf test, thus avoiding duplicate error messages if it doesn't.
onlyInteger
boolean
No
false
Specifies whether the property value must be an integer.
condition
string
No
String expression to be evaluated that decides if validation will be run (if the expression returns true validation will run).
unless
string
No
String expression to be evaluated that decides if validation will be run (if the expression returns false validation will run).
odd
boolean
No
even
boolean
No
greaterThan
numeric
No
Specifies whether or not the value must be greater than the supplied value.
greaterThanOrEqualTo
numeric
No
Specifies whether or not the value must be greater than or equal the supplied value.
equalTo
numeric
No
Specifies whether or not the value must be equal to the supplied value.
lessThan
numeric
No
Specifies whether or not the value must be less than the supplied value.
lessThanOrEqualTo
numeric
No
Specifies whether or not the value must be less than or equal the supplied value.
// 1. Validate that the `age` property is a number
validatesNumericalityOf(property="age");
// 2. Validate that the `score` property is a whole number (no decimals), allowing blank so that
// records can be saved without a score (resulting in a NULL in the database)
validatesNumericalityOf(property="score", onlyInteger=true, allowBlank=true, message="Please enter a whole number for score.");
// 3. Validate that a `price` value is greater than zero and no more than 10000
validatesNumericalityOf(property="price", greaterThan=0, lessThanOrEqualTo=10000);
// 4. Validate that `quantity` is at least 1, is an integer, and only on create
validatesNumericalityOf(property="quantity", onlyInteger=true, greaterThanOrEqualTo=1, when="onCreate");
// 5. Validate that `rating` must be exactly 5 only when a condition is met
validatesNumericalityOf(property="rating", equalTo=5, condition="this.isPerfect()");
// 6. Validate that `luckyNumber` is an odd number
validatesNumericalityOf(property="luckyNumber", odd=true, message="[property] must be an odd number.");
Validates that the specified property exists and that its value is not blank.
Name
Type
Required
Default
Description
properties
string
No
Name of property or list of property names to validate against (can also be called with the property argument).
message
string
No
[property] can't be empty
Supply a custom error message here to override the built-in one.
when
string
No
onSave
Pass in onCreate or onUpdate to limit when this validation occurs (by default validation will occur on both create and update, i.e. onSave).
condition
string
No
String expression to be evaluated that decides if validation will be run (if the expression returns true validation will run).
unless
string
No
String expression to be evaluated that decides if validation will be run (if the expression returns false validation will run).
// 1. Require a single property (must exist and not be blank)
validatesPresenceOf("emailAddress");
// 2. Require multiple properties at once
validatesPresenceOf(properties="firstName,lastName,emailAddress");
// 3. Supply a custom error message
validatesPresenceOf(properties="title", message="A title is required.");
// 4. Only validate on create (skip when updating an existing record)
validatesPresenceOf(properties="password", when="onCreate");
// 5. Conditionally require a property based on another property value
validatesPresenceOf(properties="companyName", condition="this.accountType eq 'business'");
// 6. Skip validation when a certain condition is true
validatesPresenceOf(properties="bio", unless="this.isGuest()");
Validates that the value of the specified property is unique in the database table.
Useful for ensuring that two users can't sign up to a website with identical usernames for example.
When a new record is created, a check is made to make sure that no record already exists in the database table with the given value for the specified property.
When the record is updated, the same check is made but disregarding the record itself.
Name
Type
Required
Default
Description
properties
string
No
Name of property or list of property names to validate against (can also be called with the property argument).
message
string
No
[property] has already been taken
Supply a custom error message here to override the built-in one.
when
string
No
onSave
Pass in onCreate or onUpdate to limit when this validation occurs (by default validation will occur on both create and update, i.e. onSave).
allowBlank
boolean
No
false
If set to true, validation will be skipped if the property value is an empty string or doesn't exist at all. This is useful if you only want to run this validation after it passes the validatesPresenceOf test, thus avoiding duplicate error messages if it doesn't.
scope
string
No
One or more properties by which to limit the scope of the uniqueness constraint.
condition
string
No
String expression to be evaluated that decides if validation will be run (if the expression returns true validation will run).
unless
string
No
String expression to be evaluated that decides if validation will be run (if the expression returns false validation will run).
includeSoftDeletes
boolean
No
true
Set to true to include soft-deleted records in the queries that this method runs.
// 1. Ensure no two users share the same username
validatesUniquenessOf(property="username", message="Sorry, that username is already taken.");
// 2. Scope uniqueness to an account — the same username is allowed in different accounts
validatesUniquenessOf(property="username", scope="accountId");
// 3. Validate multiple properties for uniqueness in one call
validatesUniquenessOf(properties="email,username");
// 4. Skip the check when the email field is blank (pair with validatesPresenceOf to avoid duplicate errors)
validatesUniquenessOf(property="email", allowBlank=true);
// 5. Only enforce uniqueness on create, not on update
validatesUniquenessOf(property="slug", when="onCreate");
// 6. Run the check only when a condition is true
validatesUniquenessOf(property="referralCode", condition="this.isAffiliate()");
// 7. Exclude soft-deleted records from the uniqueness check so a previously-deleted value can be reused
validatesUniquenessOf(property="username", includeSoftDeletes=false);
Returns a struct containing all validation rules for this model, keyed by trigger (onSave, onCreate, onUpdate).
Each trigger contains an array of validation rule structs with method, properties, message, and other parameters.
// 1. Inspect all validation rules defined on the User model
info = model("User").validationInfo();
// info is keyed by trigger: onSave, onCreate, onUpdate
// Each key holds an array of rule structs, e.g.:
// info.onSave[1] -> {method: "validatesPresenceOf", properties: "email", message: "can't be blank", ...}
// info.onCreate -> []
// info.onUpdate -> []
// 2. Count how many rules fire on every save
info = model("User").validationInfo();
writeOutput("Rules on save: " & arrayLen(info.onSave));
// 3. List the validation methods used across all triggers
info = model("User").validationInfo();
for (trigger in info) {
for (rule in info[trigger]) {
writeOutput(trigger & ": " & rule.method & " on " & rule.properties);
}
}
// 1. Get the validation type for a string column (e.g. firstName is varchar)
type = model("Employee").validationTypeForProperty("firstName");
// type -> "string"
// 2. Get the validation type for a numeric column (e.g. salary is integer)
type = model("Employee").validationTypeForProperty("salary");
// type -> "numeric"
// 3. Get the validation type for a date column (e.g. hireDate is a date/datetime column)
type = model("Employee").validationTypeForProperty("hireDate");
// type -> "date"
// 4. Property does not exist on the model — returns "string" as the default
type = model("Employee").validationTypeForProperty("nonExistentProperty");
// type -> "string"
Returns an array of all the verifications set on this controller in the order in which they will be executed.
// 1. Get verification chain, remove the first item, and set it back.
myVerificationChain = verificationChain();
arrayDeleteAt(myVerificationChain, 1);
setVerificationChain(myVerificationChain);
// 2. Inspect the number of verifications registered on this controller.
chain = verificationChain();
writeOutput("Verifications registered: " & arrayLen(chain));
// 3. Loop over the chain to find verifications that apply to a specific action.
chain = verificationChain();
for (item in chain) {
if (listFindNoCase(item.only, "create")) {
writeOutput("Verification applies to create: " & serializeJSON(item));
}
}
Instructs Wheels to verify that some specific criteria are met before running an action.
Note that all undeclared arguments will be passed to redirectTo() call if a handler is not specified.
Name
Type
Required
Default
Description
only
string
No
List of action names to limit this verification to.
except
string
No
List of action names to exclude this verification from.
post
any
No
Set to true to verify that this is a POST request.
get
any
No
Set to true to verify that this is a GET request.
ajax
any
No
Set to true to verify that this is an AJAX request.
cookie
string
No
Verify that the passed in variable name exists in the cookie scope.
session
string
No
Verify that the passed in variable name exists in the session scope.
params
string
No
Verify that the passed in variable name exists in the params struct.
handler
string
No
Pass in the name of a function that should handle failed verifications. The default is to just abort the request when a verification fails.
cookieTypes
string
No
List of types to check each listed cookie value against (will be passed through to your CFML engine's IsValid function).
sessionTypes
string
No
List of types to check each list session value against (will be passed through to your CFML engine's IsValid function).
paramsTypes
string
No
List of types to check each params value against (will be passed through to your CFML engine's IsValid function).
// 1. Verify that the `handleForm` action is always a POST request.
verifies(only="handleForm", post=true);
// 2. Verify that the `edit` action is a GET request, that `userId` exists in `params`, and that it is an integer.
verifies(only="edit", get=true, params="userId", paramsTypes="integer");
// 3. Same as above, but invoke a custom handler function on failure instead of aborting.
verifies(only="edit", get=true, params="userId", paramsTypes="integer", handler="accessDenied");
// 4. Same verification, but redirect to the `index` action with a flash error message on failure.
verifies(only="edit", get=true, params="userId", paramsTypes="integer", action="index", error="Invalid userId");
// 5. Verify that a session variable named `userId` exists for all actions except `login` and `register`.
verifies(except="login,register", session="userId");
// 6. Verify that the `subscribe` action is an AJAX POST request and that `email` exists in `params` as a valid email address.
verifies(only="subscribe", ajax=true, post=true, params="email", paramsTypes="email");
Scope routes under a version prefix within an API group. Creates a URL path prefix of v{number} (e.g., /api/v1/users) and a name prefix of v{number} for named route generation.
Name
Type
Required
Default
Description
number
numeric
Yes
The version number (e.g., 1 creates path prefix v1).
path
string
No
[runtime expression]
Override the path prefix. Defaults to v{number}.
name
string
No
[runtime expression]
Override the name prefix. Defaults to v{number}.
callback
any
No
A callback function to define nested routes within this version scope.
<cfscript>
// 1. Basic versioned API routes using api() and version() together
mapper()
.api()
.version(number=1)
// Route name: apiV1Users
// Example URL: /api/v1/users
.resources("users")
.end()
.version(number=2)
// Route name: apiV2Users
// Example URL: /api/v2/users
.resources("users")
.end()
.end()
.end();
// 2. Using a callback to define routes within the version scope
mapper()
.api(callback=function(r) {
r.version(number=1, callback=function(r) {
// Route name: apiV1Products
// Example URL: /api/v1/products
r.resources("products");
});
})
.end();
// 3. Overriding the path and name prefixes
mapper()
.api()
.version(number=1, path="version-one", name="versionOne")
// Route name: apiVersionOneOrders
// Example URL: /api/version-one/orders
.resources("orders")
.end()
.end()
.end();
</cfscript>
Returns the resolved URL for a Vite entrypoint. In production, reads the Vite manifest
to return the fingerprinted asset path. In development, returns the Vite dev server URL.
Name
Type
Required
Default
Description
entrypoint
string
Yes
The source entrypoint path as defined in your Vite config (e.g. "src/main.js").
// 1. Get the resolved URL for a Vite JS entrypoint
// In production, returns a fingerprinted path like "/dist/assets/main-Dz8C9a3m.js"
// In development, returns the Vite dev server URL like "http://localhost:5173/src/main.js"
assetUrl = viteAsset("src/main.js");
// 2. Use the resolved URL directly in an image or font tag
logoUrl = viteAsset("src/images/logo.png");
writeOutput('<img src="#logoUrl#" alt="Logo">');
// 3. Resolve a CSS entrypoint URL for manual use (e.g. a preload hint)
cssUrl = viteAsset("src/main.css");
writeOutput('<link rel="preload" as="style" href="#cssUrl#">');
Returns tags for a Vite entrypoint and its transitive
chunk imports. Useful for Turbo Drive hover-preload patterns or for explicitly warming
assets a subsequent navigation will need.
In development mode, returns an empty string — Vite handles module resolution
dynamically and modulepreload is unnecessary.
emits via $viteHtmlHead() so tags land in .
Name
Type
Required
Default
Description
entrypoint
string
Yes
The source entrypoint path (e.g. "src/main.js").
head
boolean
No
true
Set to false to return the markup for inline placement; default true
// 1. Emit modulepreload tags into <head> for a JS entrypoint (default behavior)
// In production, injects <link rel="modulepreload"> for the entrypoint and all
// transitive chunk imports into <head>. Returns an empty string.
// In development, returns an empty string (Vite handles modules dynamically).
vitePreloadTag("src/main.js");
// 2. Return modulepreload markup inline instead of injecting into <head>
// Pass head=false to receive the raw HTML for manual placement, for example
// inside a Turbo Drive hover-preload data attribute or a custom <head> partial.
preloadMarkup = vitePreloadTag(entrypoint="src/main.js", head=false);
// preloadMarkup -> '<link rel="modulepreload" href="/dist/assets/main-Dz8C9a3m.js" />\n<link rel="modulepreload" href="/dist/assets/vendor-BpC2d1a0.js" />\n'
// 3. Warm assets for a page the user is likely to navigate to next
// Call from a controller action to preload a separate route's entry module
// so the browser fetches chunks before the user clicks the link.
vitePreloadTag("src/checkout.js");
Returns 'script' tags for a Vite JS entrypoint. In development, also injects the Vite
client for Hot Module Replacement (HMR). In production, includes any associated CSS files
from the manifest as tags.
Name
Type
Required
Default
Description
entrypoint
string
Yes
The source entrypoint path (e.g. "src/main.js").
head
boolean
No
false
Set to true to place output in the area instead of inline.
// 1. Emit a script tag for a Vite JS entrypoint inline (default)
// In development, also injects the Vite HMR client.
// In production, emits <link> tags for any associated CSS and a <script type="module"> tag.
writeOutput(viteScriptTag("src/main.js"));
// 2. Place the script tag in the <head> instead of inline
// Passes the generated markup to $htmlHead() so it is buffered into the page <head>.
// Returns an empty string; nothing is printed at the call site.
viteScriptTag(entrypoint="src/main.js", head=true);
// 3. Emit a script tag for a page-specific entrypoint
// Each entrypoint gets its own script tag; CSS chunks discovered in the
// manifest are automatically included as <link rel="stylesheet"> tags.
writeOutput(viteScriptTag("src/checkout.js"));
Returns a tag for a Vite CSS entrypoint. In development, Vite injects CSS via
the JS client so this returns an empty string. In production, resolves the fingerprinted path.
Name
Type
Required
Default
Description
entrypoint
string
Yes
The source CSS entrypoint path (e.g. "src/main.css").
head
boolean
No
false
Set to true to place output in the area instead of inline.
// 1. Emit a <link> tag for a standalone CSS entrypoint inline (default)
// In development, returns an empty string — Vite injects CSS via the HMR JS client.
// In production, outputs a fingerprinted <link rel="stylesheet"> tag.
writeOutput(viteStyleTag("src/main.css"));
// 2. Place the <link> tag in the <head> instead of inline
// Passes the generated markup to $htmlHead() so it is buffered into the page <head>.
// Returns an empty string; nothing is printed at the call site.
viteStyleTag(entrypoint="src/main.css", head=true);
// 3. Emit a <link> tag for a page-specific CSS entrypoint
// Useful when a particular view has its own standalone stylesheet entrypoint
// defined in your Vite config alongside the primary JS bundle.
writeOutput(viteStyleTag("src/checkout.css"));
Constrain a route variable to only match alphabetic characters (a-zA-Z). Similar to Laravel's whereAlpha() or ASP.NET's :alpha constraint.
Name
Type
Required
Default
Description
variableName
string
Yes
The route variable name to constrain. Can also be a comma-delimited list.
<cfscript>
mapper()
// 1. Constrain a single route variable to alphabetic characters only
// The [locale] segment will only match values like "en", "fr", "de"
.get(name="localizedHome", pattern="[locale]/home", to="home##index")
.whereAlpha("locale")
// 2. Constrain multiple variables at once using a comma-delimited list
// Both [lang] and [region] must contain only a-z / A-Z characters
.get(name="localizedPage", pattern="[lang]/[region]/[action]", to="pages##show")
.whereAlpha("lang,region")
// 3. Chain with other constraint helpers for mixed-type route variables
// [category] must be alphabetic; [id] must be numeric
.resources(name="articles")
.get(name="articleByCategory", pattern="articles/[category]/[id]", to="articles##byCategory")
.whereAlpha("category")
.whereNumber("id")
.end();
</cfscript>
Constrain a route variable to only match alphanumeric characters (a-zA-Z0-9). Similar to Laravel's whereAlphaNumeric().
Name
Type
Required
Default
Description
variableName
string
Yes
The route variable name to constrain. Can also be a comma-delimited list.
<cfscript>
mapper()
// 1. Constrain a single route variable to alphanumeric characters only
.get(name="profile", pattern="profiles/[username]", to="profiles##show")
.whereAlphaNumeric("username")
// 2. Constrain multiple variables to alphanumeric in one call (comma-delimited list)
.get(name="teamMember", pattern="teams/[teamCode]/members/[memberCode]", to="teams##member")
.whereAlphaNumeric("teamCode,memberCode")
// 3. Chain whereAlphaNumeric with a resources block to restrict the key variable
.resources(name="products")
.whereAlphaNumeric("key")
.end();
</cfscript>
Constrain a route variable to only match one of a set of allowed values. Similar to an enum constraint.
Name
Type
Required
Default
Description
variableName
string
Yes
The route variable name to constrain.
values
string
Yes
A comma-delimited list of allowed values (e.g., "active,inactive,pending").
<cfscript>
mapper()
// 1. Constrain a route variable to a fixed set of allowed string values
// The [status] segment will only match "active", "inactive", or "pending"
.get(name="usersByStatus", pattern="users/[status]", to="users##byStatus")
.whereIn(variableName="status", values="active,inactive,pending")
// 2. Constrain a locale segment to a known list of supported languages
// Requests like /en/home match; /xx/home returns a 404
.get(name="localizedHome", pattern="[locale]/home", to="home##index")
.whereIn(variableName="locale", values="en,fr,de,es")
// 3. Chain whereIn with whereNumber for mixed-type segment constraints
// [type] must be one of the listed values; [id] must be numeric
.get(name="typedItem", pattern="items/[type]/[id]", to="items##show")
.whereIn(variableName="type", values="book,magazine,journal")
.whereNumber("id")
.end();
</cfscript>
Constrain a route variable with a custom regex pattern.
Name
Type
Required
Default
Description
variableName
string
Yes
The route variable name to constrain.
pattern
string
Yes
The regex pattern the variable must match.
<cfscript>
mapper()
// 1. Constrain a route variable to a custom regex pattern (year: 4-digit number)
.get(name="archiveYear", pattern="archive/[year]", to="posts##archiveByYear")
.whereMatch(variableName="year", pattern="\d{4}")
// 2. Constrain a slug variable to lowercase letters and hyphens only
.get(name="articleShow", pattern="articles/[slug]", to="articles##show")
.whereMatch(variableName="slug", pattern="[a-z][a-z0-9-]+")
// 3. Chained with resources — constrain the key to a specific format (e.g. SKU like AB-12345)
.resources("products")
.whereMatch(variableName="key", pattern="[A-Z]{2}-\d{5}")
.end();
</cfscript>
Constrain a route variable to only match numeric values (digits). Similar to Laravel's whereNumber() or ASP.NET's :int constraint.
Name
Type
Required
Default
Description
variableName
string
Yes
The route variable name to constrain (e.g., "id"). Can also be a comma-delimited list to constrain multiple variables.
<cfscript>
mapper()
// 1. Constrain a single route variable to numeric digits only
// The [id] segment will only match values like "1", "42", "1000"
.get(name="article", pattern="articles/[id]", to="articles##show")
.whereNumber("id")
// 2. Constrain multiple variables at once using a comma-delimited list
// Both [year] and [month] must contain only digit characters
.get(name="archiveMonth", pattern="archive/[year]/[month]", to="posts##archive")
.whereNumber("year,month")
// 3. Chain with other constraint helpers for mixed-type route variables
// [category] must be alphabetic; [id] must be numeric
.get(name="categoryItem", pattern="[category]/[id]", to="items##show")
.whereAlpha("category")
.whereNumber("id")
.end();
</cfscript>
Constrain a route variable to only match URL-friendly slug values (lowercase alphanumeric and hyphens).
Name
Type
Required
Default
Description
variableName
string
Yes
The route variable name to constrain. Can also be a comma-delimited list.
<cfscript>
mapper()
// 1. Constrain a single slug variable — matches "my-article-title" but not "My Article" or "abc123!"
.get(name="article", to="articles##show")
.whereSlug("slug")
// 2. Chain with another constraint helper after a resource route
.resources(name="posts")
.whereSlug("postSlug")
// 3. Constrain multiple slug variables at once using a comma-delimited list
.get(name="categoryPost", pattern="[category]/[postSlug]", to="posts##showByCategory")
.whereSlug("category,postSlug")
.end();
</cfscript>
Constrain a route variable to only match UUID values. Similar to ASP.NET's :guid constraint.
Name
Type
Required
Default
Description
variableName
string
Yes
The route variable name to constrain. Can also be a comma-delimited list.
<cfscript>
mapper()
// 1. Constrain a single route variable to UUID values only
// The [id] segment will only match values like "550e8400-e29b-41d4-a716-446655440000"
.get(name="document", pattern="documents/[id]", to="documents##show")
.whereUuid("id")
// 2. Constrain multiple variables to UUID format using a comma-delimited list
// Both [userId] and [sessionId] must be valid UUID values
.get(name="userSession", pattern="users/[userId]/sessions/[sessionId]", to="sessions##show")
.whereUuid("userId,sessionId")
// 3. Chain with other constraint helpers for mixed-type route variables
// [type] must be alphabetic; [id] must be a UUID
.get(name="typedResource", pattern="resources/[type]/[id]", to="resources##show")
.whereAlpha("type")
.whereUuid("id")
.end();
</cfscript>
List of HTTP methods (verbs) to generate the wildcard routes for. We strongly recommend leaving the default value of get and using other routing mappers if you need to POST to a URL endpoint. Pass an empty string to generate the wildcard routes for all verbs (get, post, put, patch, and delete).
action
string
No
index
Default action to specify if the value for the [action] placeholder is not provided.
mapKey
boolean
No
false
Whether or not to enable a [key] matcher, enabling a [controller]/[action]/[key] pattern.
mapFormat
boolean
No
false
Whether or not to add an optional .[format] pattern to the end of the generated routes. This is useful for providing formats via URL like json, xml, pdf, etc.
methods
string
No
Alias for method, provided for better readability when listing multiple methods. Takes precedence over method when both are passed.
<cfscript>
mapper()
// 1. Basic wildcard: enables `[controller]` and `[controller]/[action]`
// patterns via GET requests only.
.wildcard()
// 2. Also enable a `[controller]/[action]/[key]` pattern.
.wildcard(mapKey=true)
// 3. Add an optional `.[format]` suffix to every generated pattern,
// e.g. `[controller]/[action].json`.
.wildcard(mapFormat=true)
// 4. Change the default action when only `[controller]` is matched.
// Requests to `/photos` will route to `photos##home` instead of
// `photos##index`.
.wildcard(action="home")
// 5. Allow additional HTTP methods beyond GET.
// Note: opening up extra methods can create security holes unless
// you use `verifies` in your controller to guard data-changing actions.
.wildcard(method="get,post")
.end();
</cfscript>
Executes a callback while holding a database advisory lock.
The lock is automatically released when the callback completes, even if an exception is thrown.
Advisory locks are database-level locks that don't lock rows or tables. They are useful for
coordinating exclusive access to shared resources across application instances.
Support varies by database:
- PostgreSQL: Full support via pg_advisory_lock/pg_advisory_unlock
- MySQL: Full support via GET_LOCK/RELEASE_LOCK
- SQL Server: Full support via sp_getapplock/sp_releaseapplock
- SQLite: No-op (file-level locking only)
- CockroachDB: Not supported (throws error, use forUpdate() instead)
- H2: Not supported (throws error)
- Oracle: Not supported by default (requires DBMS_LOCK package setup)
Name
Type
Required
Default
Description
name
string
Yes
A unique name for the lock. Different callers using the same name will contend for the same lock.
timeout
numeric
No
10
Maximum number of seconds to wait when acquiring the lock (supported by MySQL and SQL Server).
callback
any
Yes
A function or closure to execute while holding the lock. Its return value is returned by this method.
// 1. Prevent duplicate processing of a background job
model("Job").withAdvisoryLock(name="process-nightly-report", callback=function() {
job = model("Job").findOneByNameAndStatus(name="nightly-report", status="pending");
if (isObject(job)) {
job.process();
}
});
// 2. Serialize access to a shared external resource with a custom timeout
result = model("Payment").withAdvisoryLock(
name="payment-gateway-sync",
timeout=30,
callback=function() {
return model("Payment").syncWithGateway();
}
);
// 3. Ensure only one instance assigns the next batch of records
model("Task").withAdvisoryLock(name="task-batch-assignment", callback=function() {
tasks = model("Task").findAll(
conditions="assignedTo IS NULL",
maxRows=10,
returnAs="objects"
);
for (task in tasks) {
task.update(assignedTo=getCurrentWorkerID());
}
});
Truncates text to the specified length of words and replaces the remaining characters with the specified truncate string (which defaults to "...").
Name
Type
Required
Default
Description
text
string
Yes
The text to truncate.
length
numeric
No
5
Number of words to truncate the text to.
truncateString
string
No
...
String to replace the last characters with.
// 1. Truncate text to the first 4 words (default truncate string "...")
result = wordTruncate(text="CFWheels is a framework for ColdFusion", length=4);
// result -> "CFWheels is a framework..."
// 2. Truncate with a custom truncate string
result = wordTruncate(text="The quick brown fox jumps over the lazy dog", length=5, truncateString=" [read more]");
// result -> "The quick brown fox jumps [read more]"
// 3. Text with fewer words than the limit is returned unchanged
result = wordTruncate(text="Short text", length=10);
// result -> "Short text"
Builds and returns a string containing a select form control for a range of years based on the supplied name.
Name
Type
Required
Default
Description
name
string
Yes
Name to populate in tag's name attribute.
selected
string
No
The year that should be selected initially.
startYear
numeric
No
2021
First year in select list.
endYear
numeric
No
2031
Last year in select list.
includeBlank
any
No
false
Whether to include a blank option in the select form control. Pass true to include a blank line or a string that should represent what display text should appear for the empty value (for example, "- Select One -").
label
string
No
The label text to use in the form control.
labelPlacement
string
No
around
Whether to place the label before, after, or wrapped around the form control. Label text placement can be controlled using aroundLeft or aroundRight.
prepend
string
No
String to prepend to the form control. Useful to wrap the form control with HTML tags.
append
string
No
String to append to the form control. Useful to wrap the form control with HTML tags.
prependToLabel
string
No
String to prepend to the form control's label. Useful to wrap the form control with HTML tags.
appendToLabel
string
No
String to append to the form control's label. Useful to wrap the form control with HTML tags.
encode
any
No
true
Use this argument to decide whether the output of the function should be encoded in order to prevent Cross Site Scripting (XSS) attacks. Set it to true to encode all relevant output for the specific HTML element in question (e.g. tag content, attribute values, and URLs). For HTML elements that have both tag content and attribute values you can set this argument to attributes to only encode attribute values and not tag content.
// 1. Basic year select tag using the current request params as the selected value
#yearSelectTag(name="yearOfBirthday", selected=params.yearOfBirthday)#
// 2. Restrict the range to the past 50 years with a minimum of 18 years ago
#yearSelectTag(
name="yearOfBirthday",
selected=params.yearOfBirthday,
startYear=Year(Now()) - 50,
endYear=Year(Now()) - 18
)#
// 3. Include a blank prompt and wrap with a label
#yearSelectTag(
name="yearOfBirthday",
selected=params.yearOfBirthday,
includeBlank="- Select Year -",
label="Birth Year",
labelPlacement="before"
)#