Loading...
Ask or search...
Ctrl K
Loading...

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 equals flag=true (e.g., --migration equals migration=true)
  • Flag with value: --flag=value equals flag=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

  1. Naming: Use singular names (User, not Users)
  2. Properties: Define all database columns with correct types
  3. Validations: Add comprehensive validations in model code
  4. Associations: Define all relationships using PascalCase
  5. Callbacks: Use for automatic behaviors
  6. Indexes: Add to migration for performance
  7. 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"

  1. Check that name is not empty: name=User
  2. Ensure PascalCase format: name=User (not name=user)
  3. Use singular form: name=User (not name=Users)
  4. Remove special characters: name=BlogPost (not name="Blog Post")

Properties Not Generated in Migration

  1. Check property format: properties="name:string,email:string"
  2. Ensure valid property types (see Property Types table above)
  3. Remove extra spaces: name:string (not name: string)
  4. Use comma separators: name:string,email:string

Relationships Not Working

  1. Use PascalCase model names: belongsTo="User" (not belongsTo="user")
  2. Remove spaces after commas: belongsTo="User,Category"
  3. Ensure referenced models exist or will be created

Migration Not Generated

  1. Check --migration=false wasn't set
  2. Ensure you have write permissions in the directory
  3. Verify migration directory exists: /app/migrator/migrations/

Boolean Parameters Not Working

  1. Use --flag for flag=true: --force equals force=true
  2. Use --flag=false for false values: --migration=false
  3. Don't mix syntaxes: Use all flags or all named parameters

See Also