Object Relational Mapping
Creating Records
Reading Records
Updating Records
Deleting Records
Column Statistics
Dynamic Finders
Getting Paginated Data
Associations
Nested Properties
Object Validation
Object Callbacks
Calculated Properties
Transactions
Dirty Records
Soft Delete
Automatic Time Stamps
Using Multiple Data Sources
wheels generate model
Generate a model with properties, validations, and associations.
Synopsis
wheels generate model name=<modelName> [options]
#Can also be used as:
wheels g model name=<modelName> [options]
Parameter Syntax
CommandBox supports multiple parameter formats:
- Named parameters:
name=value
(e.g.,name=User
,properties=name:string,email:string
) - Flag parameters:
--flag
equalsflag=true
(e.g.,--migration
equalsmigration=true
) - Flag with value:
--flag=value
equalsflag=value
(e.g.,--primaryKey=uuid
)
Note: Flag syntax (--flag
) avoids positional/named parameter conflicts and is recommended for boolean options.
Description
The wheels generate model
command creates a new model CFC file with optional properties, associations, and database migrations. Models represent database tables and contain business logic, validations, and relationships.
Arguments
| Argument | Description | Default |
|----------|-------------|---------|
| name
| Model name (singular) | Required |
Options
| Option | Description | Valid Values | Default |
|--------|-------------|--------------|---------|
| properties
| Model properties (format: name:type,name2:type2) | Property format: name:type[,name2:type2]
where type is valid column type | ""
|
| belongsTo
| Parent model relationships (comma-separated) | Valid model names (PascalCase), comma-separated | ""
|
| hasMany
| Child model relationships (comma-separated) | Valid model names (PascalCase), comma-separated | ""
|
| hasOne
| One-to-one relationships (comma-separated) | Valid model names (PascalCase), comma-separated | ""
|
| primaryKey
| Primary key column name(s) | Valid column name (alphanumeric, underscore) | id
|
| tableName
| Custom database table name | Valid table name (alphanumeric, underscore) | ""
|
| description
| Model description | Any descriptive text | ""
|
| migration
| Generate database migration | true
, false
| true
|
| force
| Overwrite existing files | true
, false
| false
|
Parameter Validation
Required Parameters
name
: Cannot be empty, must be valid CFML component name (alphanumeric, starts with letter)
Property Types Validation
Valid property types for the properties
parameter:
| Type | Database Type | Validation |
|------|---------------|------------|
| string
| VARCHAR(255) | Default string type |
| text
| TEXT | For longer text content |
| integer
| INTEGER | Whole numbers |
| biginteger
| BIGINT | Large whole numbers |
| float
| FLOAT | Decimal numbers |
| decimal
| DECIMAL(10,2) | Precise decimal numbers |
| boolean
| BOOLEAN | true/false values |
| date
| DATE | Date values |
| datetime
| DATETIME | Date and time values |
| timestamp
| TIMESTAMP | Timestamp values |
| binary
| BLOB | Binary data |
| uuid
| VARCHAR(35) | UUID strings |
Model Name Validation
- Must be singular (User, not Users)
- Must be PascalCase (User, BlogPost)
- Cannot contain spaces or special characters
- Must be valid CFML component name
Relationship Validation
- Relationship model names must follow model naming conventions
- Models referenced in relationships should exist or be created
- Comma-separated values cannot contain spaces around commas
Examples
Basic model
wheels generate model name=User
Creates:
/models/User.cfc
- Migration file (if enabled)
Model with properties
wheels generate model name=User --properties="firstName:string,lastName:string,email:string,age:integer"
Model with associations
wheels generate model name=Post --belongsTo="User" --hasMany="Comments"
Model without migration
wheels generate model name=Setting --migration=false
Complex model
wheels generate model name=Product --properties="name:string,price:decimal,stock:integer,active:boolean" --belongsTo="Category,Brand" --hasMany="Reviews,OrderItems"
Validation Examples
✅ Valid Examples
# Valid model name and properties
wheels generate model name=User --properties="firstName:string,lastName:string,email:string,age:integer"
# Valid relationships
wheels generate model name=Post --belongsTo="User,Category" --hasMany="Comments,Tags"
# Valid property types
wheels generate model name=Product --properties="name:string,description:text,price:decimal,inStock:boolean,createdAt:datetime"
❌ Invalid Examples and Errors
Invalid Model Names
# Error: Model name cannot be empty
wheels generate model name=""
# Result: Invalid model name error
# Error: Model name should be singular
wheels generate model name=Users
# Result: Warning about plural name
# Error: Invalid characters
wheels generate model name="Blog Post"
# Result: Invalid model name error
Invalid Property Types
# Error: Invalid property type
wheels generate model name=User --properties="name:varchar,age:int"
# Result: Use 'string' instead of 'varchar', 'integer' instead of 'int'
# Error: Missing property type
wheels generate model name=User --properties="name,email:string"
# Result: Property format must be 'name:type'
Invalid Relationships
# Error: Invalid model name format
wheels generate model name=Post --belongsTo="user,blog_category"
# Result: Use PascalCase: 'User,BlogCategory'
# Error: Spaces in comma-separated values
wheels generate model name=Post --belongsTo="User, Category"
# Result: Remove spaces: 'User,Category'
Property Types
| Type | Database Type | CFML Type |
|------|---------------|-----------|
| string
| VARCHAR(255) | string |
| text
| TEXT | string |
| integer
| INTEGER | numeric |
| biginteger
| BIGINT | numeric |
| float
| FLOAT | numeric |
| decimal
| DECIMAL(10,2) | numeric |
| boolean
| BOOLEAN | boolean |
| date
| DATE | date |
| datetime
| DATETIME | date |
| timestamp
| TIMESTAMP | date |
| binary
| BLOB | binary |
| uuid
| VARCHAR(35) | string |
Generated Code
Basic Model
component extends="Model" {
function init() {
// Table name (optional if following conventions)
table("users");
// Validations
validatesPresenceOf("email");
validatesUniquenessOf("email");
validatesFormatOf("email", regex="^[^@]+@[^@]+\.[^@]+$");
// Callbacks
beforeCreate("setDefaultValues");
}
private function setDefaultValues() {
if (!StructKeyExists(this, "createdAt")) {
this.createdAt = Now();
}
}
}
Model with Properties
component extends="Model" {
function init() {
// Properties
property(name="firstName", label="First Name");
property(name="lastName", label="Last Name");
property(name="email", label="Email Address");
property(name="age", label="Age");
// Validations
validatesPresenceOf("firstName,lastName,email");
validatesUniquenessOf("email");
validatesFormatOf("email", regex="^[^@]+@[^@]+\.[^@]+$");
validatesNumericalityOf("age", onlyInteger=true, greaterThan=0, lessThan=150);
}
}
Model with Associations
component extends="Model" {
function init() {
// Associations
belongsTo("user");
hasMany("comments", dependent="deleteAll");
// Nested properties
nestedProperties(associations="comments", allowDelete=true);
// Validations
validatesPresenceOf("title,content,userId");
validatesLengthOf("title", maximum=255);
}
}
Validations
Common validation methods:
// Presence
validatesPresenceOf("name,email");
// Uniqueness
validatesUniquenessOf("email,username");
// Format
validatesFormatOf("email", regex="^[^@]+@[^@]+\.[^@]+$");
validatesFormatOf("phone", regex="^\d{3}-\d{3}-\d{4}$");
// Length
validatesLengthOf("username", minimum=3, maximum=20);
validatesLengthOf("bio", maximum=500);
// Numerical
validatesNumericalityOf("age", onlyInteger=true, greaterThan=0);
validatesNumericalityOf("price", greaterThan=0);
// Inclusion/Exclusion
validatesInclusionOf("status", list="active,inactive,pending");
validatesExclusionOf("username", list="admin,root,system");
// Confirmation
validatesConfirmationOf("password");
// Custom
validate("customValidation");
Associations
Belongs To
belongsTo("user");
belongsTo(name="author", modelName="user", foreignKey="authorId");
Has Many
hasMany("comments");
hasMany(name="posts", dependent="deleteAll", orderBy="createdAt DESC");
Has One
hasOne("profile");
hasOne(name="address", dependent="delete");
Many to Many
hasMany("categorizations");
hasMany(name="categories", through="categorizations");
Callbacks
Lifecycle callbacks:
// Before callbacks
beforeCreate("method1,method2");
beforeUpdate("method3");
beforeSave("method4");
beforeDelete("method5");
beforeValidation("method6");
// After callbacks
afterCreate("method7");
afterUpdate("method8");
afterSave("method9");
afterDelete("method10");
afterValidation("method11");
afterFind("method12");
afterInitialization("method13");
Generated Migration
When --migration
is enabled:
component extends="wheels.migrator.Migration" {
function up() {
transaction {
t = createTable("users");
t.string("firstName");
t.string("lastName");
t.string("email");
t.integer("age");
t.timestamps();
t.create();
addIndex(table="users", columnNames="email", unique=true);
}
}
function down() {
transaction {
dropTable("users");
}
}
}
Common Validation Errors
Model Name Errors
- Empty name:
name=""
→ Provide a valid model name - Plural names:
name=Users
→ Use singular form:name=User
- Invalid characters:
name="Blog Post"
→ Use PascalCase:name=BlogPost
- Lowercase:
name=user
→ Use PascalCase:name=User
Property Format Errors
- Missing colon:
properties="name,email:string"
→properties="name:string,email:string"
- Invalid types:
properties="name:varchar"
→properties="name:string"
- Extra spaces:
properties="name: string"
→properties="name:string"
- Missing type:
properties="name:"
→properties="name:string"
Boolean Parameter Errors
- Invalid boolean:
--migration=yes
→--migration=true
or--migration=false
- Mixed syntax:
migration=true --force
→--migration=true --force
Relationship Format Errors
- Lowercase models:
belongsTo="user"
→belongsTo="User"
- Extra spaces:
belongsTo="User, Category"
→belongsTo="User,Category"
- Invalid separators:
belongsTo="User;Category"
→belongsTo="User,Category"
Best Practices
- Naming: Use singular names (User, not Users)
- Properties: Define all database columns with correct types
- Validations: Add comprehensive validations in model code
- Associations: Define all relationships using PascalCase
- Callbacks: Use for automatic behaviors
- Indexes: Add to migration for performance
- Validation: Always validate parameters before running command
Common Patterns
Soft Deletes
function init() {
softDeletes();
}
Calculated Properties
function init() {
property(name="fullName", sql="firstName + ' ' + lastName");
}
Scopes
function scopeActive() {
return where("active = ?", [true]);
}
function scopeRecent(required numeric days=7) {
return where("createdAt >= ?", [DateAdd("d", -arguments.days, Now())]);
}
Default Values
function init() {
beforeCreate("setDefaults");
}
private function setDefaults() {
if (!StructKeyExists(this, "status")) {
this.status = "pending";
}
if (!StructKeyExists(this, "priority")) {
this.priority = 5;
}
}
Testing
Generate model tests:
wheels generate model name=User --properties="email:string,name:string"
wheels generate test model name=User
Troubleshooting
Command Fails with "Invalid model name"
- Check that name is not empty:
name=User
- Ensure PascalCase format:
name=User
(notname=user
) - Use singular form:
name=User
(notname=Users
) - Remove special characters:
name=BlogPost
(notname="Blog Post"
)
Properties Not Generated in Migration
- Check property format:
properties="name:string,email:string"
- Ensure valid property types (see Property Types table above)
- Remove extra spaces:
name:string
(notname: string
) - Use comma separators:
name:string,email:string
Relationships Not Working
- Use PascalCase model names:
belongsTo="User"
(notbelongsTo="user"
) - Remove spaces after commas:
belongsTo="User,Category"
- Ensure referenced models exist or will be created
Migration Not Generated
- Check
--migration=false
wasn't set - Ensure you have write permissions in the directory
- Verify migration directory exists:
/app/migrator/migrations/
Boolean Parameters Not Working
- Use
--flag
forflag=true
:--force
equalsforce=true
- Use
--flag=false
for false values:--migration=false
- Don't mix syntaxes: Use all flags or all named parameters
See Also
- wheels dbmigrate create table - Create migrations
- wheels generate property - Add properties to existing models
- wheels generate controller - Generate controllers
- wheels scaffold - Generate complete CRUD
- Synopsis
- Parameter Syntax
- Arguments
- Options
- Parameter Validation
- Required Parameters
- Property Types Validation
- Model Name Validation
- Relationship Validation
- Examples
- Basic model
- Model with properties
- Model with associations
- Model without migration
- Complex model
- Validation Examples
- ✅ Valid Examples
- ❌ Invalid Examples and Errors
- Property Types
- Generated Code
- Basic Model
- Model with Properties
- Model with Associations
- Validations
- Associations
- Belongs To
- Has Many
- Has One
- Many to Many
- Callbacks
- Generated Migration
- Common Validation Errors
- Model Name Errors
- Property Format Errors
- Boolean Parameter Errors
- Relationship Format Errors
- Best Practices
- Common Patterns
- Soft Deletes
- Calculated Properties
- Scopes
- Default Values
- Testing
- Troubleshooting
- Command Fails with "Invalid model name"
- Properties Not Generated in Migration
- Relationships Not Working
- Migration Not Generated
- Boolean Parameters Not Working
- See Also