wheels generate migration
Generate database migration files using templates.
Synopsis
wheels generate migration name=<migrationName> [options]
# Can also be used as:
wheels g migration name=<migrationName> [options]CommandBox Parameter Syntax
Named parameters:
param=value(e.g.,name=CreateUsersTable,table=users)Flag parameters:
--flagequalsflag=true(e.g.,--forceequalsforce=true)Param with value:
--param=valueequalsparam=value(e.g.,--description="User management")
Recommended: Use named parameters: wheels generate migration name=CreateUsersTable --create=users
Description
The wheels generate migration command creates database migration files using pre-built templates. Migrations provide version control for your database schema, allowing you to incrementally modify your database structure and roll back changes when needed. The command intelligently detects migration type from the name pattern and uses appropriate templates with transaction handling and error management.
Arguments
name
Migration name (e.g., CreateUsersTable, AddEmailToUsers)
Required
Migration Name Conventions
Must start with a letter
Can contain letters, numbers, and underscores
Use PascalCase for readability
Should describe the migration action clearly
Common Patterns:
Create[Table]Table- Creates a new tableAdd[Column]To[Table]- Adds column(s) to existing tableRemove[Column]From[Table]- Removes column(s) from tableRename[OldColumn]To[NewColumn]- Renames a columnChange[Column]In[Table]- Modifies column type/propertiesCreateIndexOn[Table]- Adds an indexRemoveIndexFrom[Table]- Removes an index
Options
create
Table name to create (forces create_table type)
Valid table name
""
table
Table name to modify (for column operations)
Valid table name
""
drop
Table name to drop (forces remove_table type)
Valid table name
""
description
Migration description (added as hint)
Any descriptive text
""
force
Overwrite existing migration file
true, false
false
Migration Types and Templates
The command automatically detects the migration type and uses the appropriate template:
Create Table
create-table.txt
Create*Table
CreateUsersTable
Remove Table
remove-table.txt
Drop*Table, Remove*Table
DropUsersTable
Add Column
create-column.txt
Add*To*
AddEmailToUsers
Remove Column
remove-column.txt
Remove*From*
RemoveAgeFromUsers
Change Column
change-column.txt
Change*In*
ChangeNameInUsers
Rename Column
rename-column.txt
Rename*To*
RenameFirstnameToFullname
Add Index
create-index.txt
CreateIndexOn*
CreateIndexOnUsersEmail
Remove Index
remove-index.txt
RemoveIndexFrom*
RemoveIndexFromUsers
Blank
blank.txt
Any other pattern
CustomMigration
Examples
Create Table Migration
wheels generate migration name=CreateUsersTableCreates migration using create-table.txt template with transaction handling.
Create Table with Explicit Table Name
wheels generate migration name=CreateUsersTable --create=usersForces create_table type and uses "users" as table name.
Add Column Migration
wheels generate migration name=AddEmailToUsers --table=usersCreates migration using create-column.txt template.
Remove Column Migration
wheels generate migration name=RemoveAgeFromUsers --table=usersCreates migration using remove-column.txt template.
Drop Table Migration
wheels generate migration name=DropProductsTableCreates migration using remove-table.txt template.
With Description
wheels generate migration name=CreateUsersTable --description="User authentication table"Adds description as hint to the migration component.
Blank/Custom Migration
wheels generate migration name=MigrateUserData --description="Move user data to new structure"Creates blank migration for custom migration code.
Force Overwrite
wheels generate migration name=CreateUsersTable --force=trueOverwrites existing migration file.
Generated Code Examples
Create Table Migration (Using Template)
component extends="wheels.migrator.Migration" hint="CreateUsersTable" {
function up() {
transaction {
try {
t = createTable(name = 'users', force='false', id='true', primaryKey='id');
t.timestamps();
t.create();
} catch (any e) {
local.exception = e;
}
if (StructKeyExists(local, "exception")) {
transaction action="rollback";
Throw(errorCode = "1", detail = local.exception.detail, message = local.exception.message, type = "any");
} else {
transaction action="commit";
}
}
}
function down() {
transaction {
try {
dropTable('users');
} catch (any e) {
local.exception = e;
}
if (StructKeyExists(local, "exception")) {
transaction action="rollback";
Throw(errorCode = "1", detail = local.exception.detail, message = local.exception.message, type = "any");
} else {
transaction action="commit";
}
}
}
}Add Column Migration (Using Template)
component extends="wheels.migrator.Migration" hint="AddEmailToUsers" {
function up() {
transaction {
try {
addColumn(table='users', columnType='string', columnName='column_name', default='', null='true', limit='255');
} catch (any e) {
local.exception = e;
}
if (StructKeyExists(local, "exception")) {
transaction action="rollback";
Throw(errorCode = "1", detail = local.exception.detail, message = local.exception.message, type = "any");
} else {
transaction action="commit";
}
}
}
function down() {
transaction {
try {
removeColumn(table='users', columnName='column_name');
} catch (any e) {
local.exception = e;
}
if (StructKeyExists(local, "exception")) {
transaction action="rollback";
Throw(errorCode = "1", detail = local.exception.detail, message = local.exception.message, type = "any");
} else {
transaction action="commit";
}
}
}
}Blank Migration (Using Template)
component extends="wheels.migrator.Migration" hint="MigrateUserData" {
function up() {
transaction {
try {
// your code goes here
} catch (any e) {
local.exception = e;
}
if (StructKeyExists(local, "exception")) {
transaction action="rollback";
Throw(errorCode = "1", detail = local.exception.detail, message = local.exception.message, type = "any");
} else {
transaction action="commit";
}
}
}
function down() {
transaction {
try {
// your code goes here
} catch (any e) {
local.exception = e;
}
if (StructKeyExists(local, "exception")) {
transaction action="rollback";
Throw(errorCode = "1", detail = local.exception.detail, message = local.exception.message, type = "any");
} else {
transaction action="commit";
}
}
}
}Smart Name Detection
The command infers table names from migration name patterns:
Pattern Detection Examples
CreateUsersTable
create_table
users
create-table.txt
DropProductsTable
remove_table
products
remove-table.txt
AddEmailToUsers
create_column
users
create-column.txt
RemoveAgeFromUsers
remove_column
users
remove-column.txt
RenameFirstnameToFullname
rename_column
(needs --table)
rename-column.txt
ChangeStatusInOrders
change_column
orders
change-column.txt
CreateIndexOnUsersEmail
create_index
users
create-index.txt
CustomDataMigration
blank
N/A
blank.txt
Template System
All migrations use templates from /cli/src/templates/dbmigrate/:
Available Templates
blank.txt- Empty migration with transaction wrappercreate-table.txt- Create table with timestampsremove-table.txt- Drop table with reversible down()create-column.txt- Add column with optionsremove-column.txt- Remove columnchange-column.txt- Modify column type/propertiesrename-column.txt- Rename columncreate-index.txt- Add index to tableremove-index.txt- Remove index from table
Template Customization
You can override default templates by placing custom versions in /app/snippets/dbmigrate/:
# Copy template to customize
cp cli/src/templates/dbmigrate/create-table.txt app/snippets/dbmigrate/create-table.txt
# Edit your custom template
# Wheels will automatically use your versionTemplate Variables
Templates use placeholder variables that are replaced during generation:
|DBMigrateDescription|
Migration hint/description
CreateUsersTable
|tableName|
Table name
users
|columnName|
Column name
|columnType|
Column type
string
|force|
Force table creation
false
|id|
Auto-create ID column
true
|primaryKey|
Primary key name
id
|allowNull|
Allow NULL values
true
|limit|
Column length limit
255
|default|
Default value
""
|unique|
Unique index
false
Migration Workflow
1. Generate Migration
wheels generate migration name=CreateUsersTable2. Edit Migration File
# File created at: /app/migrator/migrations/20250119123045_CreateUsersTable.cfcAdd columns to the migration:
function up() {
transaction {
try {
t = createTable(name = 'users', force='false', id='true', primaryKey='id');
t.string(columnNames='firstName,lastName', allowNull=false);
t.string(columnNames='email', allowNull=false, limit=100);
t.boolean(columnNames='active', default=true);
t.timestamps();
t.create();
// Add index on email
addIndex(table='users', columnNames='email', unique=true);
} catch (any e) {
local.exception = e;
}
if (StructKeyExists(local, "exception")) {
transaction action="rollback";
Throw(errorCode = "1", detail = local.exception.detail, message = local.exception.message, type = "any");
} else {
transaction action="commit";
}
}
}3. Check Migration Status
wheels dbmigrate info4. Run Migration
wheels dbmigrate latest5. Rollback if Needed
wheels dbmigrate downColumn Types
Common column types available in migrations:
t.string()
VARCHAR(255)
t.string(columnNames='email')
t.text()
TEXT
t.text(columnNames='bio')
t.integer()
INTEGER
t.integer(columnNames='age')
t.biginteger()
BIGINT
t.biginteger(columnNames='views')
t.decimal()
DECIMAL
t.decimal(columnNames='price', precision=10, scale=2)
t.float()
FLOAT
t.float(columnNames='rating')
t.boolean()
BOOLEAN
t.boolean(columnNames='active')
t.date()
DATE
t.date(columnNames='birthdate')
t.datetime()
DATETIME
t.datetime(columnNames='registeredAt')
t.time()
TIME
t.time(columnNames='startTime')
t.binary()
BLOB
t.binary(columnNames='avatar')
t.timestamps()
DATETIME
Creates createdAt and updatedAt
Best Practices
Descriptive Names: Use clear, action-oriented migration names
One Change Per Migration: Keep migrations focused on single changes
Always Include down(): Make migrations reversible when possible
Use Transactions: Templates include transactions by default
Test Rollbacks: Always test
down()method works correctlyIndex Performance Columns: Add indexes on frequently queried columns
Document Complex Migrations: Use description parameter for clarity
Avoid Data in Migrations: Keep migrations schema-only when possible
Common Patterns
Create Table with Relationships
function up() {
transaction {
try {
t = createTable(name = 'posts', force='false', id='true');
t.string(columnNames='title', allowNull=false);
t.text(columnNames='content');
t.references(name='userId', references='users');
t.boolean(columnNames='published', default=false);
t.timestamps();
t.create();
addIndex(table='posts', columnNames='userId');
} catch (any e) {
local.exception = e;
}
// Transaction handling...
}
}Add Multiple Columns
function up() {
transaction {
try {
addColumn(table='users', columnType='string', columnName='phoneNumber', limit=20);
addColumn(table='users', columnType='boolean', columnName='emailVerified', default=false);
addColumn(table='users', columnType='datetime', columnName='lastLoginAt');
addIndex(table='users', columnNames='phoneNumber');
} catch (any e) {
local.exception = e;
}
// Transaction handling...
}
}Data Migration
function up() {
transaction {
try {
// Add new column
addColumn(table='users', columnType='string', columnName='fullName');
// Migrate data (use with caution)
execute("UPDATE users SET fullName = CONCAT(firstName, ' ', lastName)");
// Remove old columns
removeColumn(table='users', columnName='firstName');
removeColumn(table='users', columnName='lastName');
} catch (any e) {
local.exception = e;
}
// Transaction handling...
}
}Error Handling
Migration Already Exists
wheels generate migration name=CreateUsersTable
# Error: Migration file already exists. Use force=true to overwrite.
# Solution:
wheels generate migration name=CreateUsersTable --force=trueInvalid Migration Name
wheels generate migration name="Create Users Table"
# Error: Invalid migration name. Use only letters, numbers, and underscores.
# Solution:
wheels generate migration name=CreateUsersTableMigration Fails to Run
wheels dbmigrate latest
# Error during migration execution
# Check the migration file for syntax errors
# Review database logs
# Test the down() method
wheels dbmigrate downTemplate Benefits
Using templates provides several advantages:
Transaction Safety - All operations wrapped in transactions Error Handling - Automatic try/catch with rollback Consistency - Same structure across all migrations Best Practices - Templates follow Wheels conventions Customizable - Override templates in /app/snippets/ Maintainability - Centralized template updates
See Also
wheels dbmigrate latest - Run migrations
wheels dbmigrate info - Check migration status
wheels dbmigrate create table - Alternative table creation
wheels dbmigrate create blank - Alternative blank migration
wheels generate model - Generate models
Database Migrations Guide - Migration best practices
Template System Guide - Customize templates
Last updated
Was this helpful?

