Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 9 additions & 14 deletions project_types/wheels/standards/code-style/cfml-style.md
Original file line number Diff line number Diff line change
Expand Up @@ -197,8 +197,8 @@ component extends="Model" {
softDelete(true);

// Automatic timestamps
timeStampOnCreate(true);
timeStampOnUpdate(true);
set(timeStampOnCreateProperty="createdAt");
set(timeStampOnUpdateProperty="updatedAt");
}

/**
Expand All @@ -212,7 +212,7 @@ component extends="Model" {
* Check if user has specific role
*/
function hasRole(required string roleName) {
return this.roles().exists(where="name = ?", whereParams=[arguments.roleName]);
return this.roles().exists(where="name = '#arguments.roleName#'");
}

/**
Expand Down Expand Up @@ -358,8 +358,7 @@ function create() {
```cfml
// Finding records
users = model("User").findAll(
where="isActive = ? AND createdAt > ?",
whereParams=[true, DateAdd("d", -30, Now())],
where="isActive = true AND createdAt > '#DateAdd("d", -30, Now())#'",
order="lastName ASC, firstName ASC",
include="profile,posts",
page=1,
Expand Down Expand Up @@ -590,14 +589,13 @@ component extends="Controller" {
// Efficient data loading with specific selects
users = model("User").findAll(
select="id, firstName, lastName, email",
where="isActive = ?",
whereParams=[true],
where="isActive = true",
include="profile", // Eager load to avoid N+1
order="lastName ASC"
);

// Use exists() instead of count() for boolean checks
hasActiveUsers = model("User").exists(where="isActive = ?", whereParams=[true]);
hasActiveUsers = model("User").exists(where="isActive = true");

// Pagination for large datasets
pagedUsers = model("User").findAll(
Expand Down Expand Up @@ -664,14 +662,12 @@ function create() {
```cfml
// Always use parameterized queries
users = model("User").findAll(
where="lastName = ? AND isActive = ?",
whereParams=[params.lastName, true] // Parameters are automatically escaped
where="lastName = '#params.lastName#' AND isActive = true"
);

// For complex queries
users = model("User").findAll(
where="firstName LIKE ? OR lastName LIKE ?",
whereParams=["%#params.search#%", "%#params.search#%"]
where="firstName LIKE '%#params.search#%' OR lastName LIKE '%#params.search#%'"
);
```

Expand Down Expand Up @@ -746,8 +742,7 @@ function processData() {

// DO: Use parameterized queries
users = model("User").findAll(
where="name = ?",
whereParams=[params.name]
where="name = '#params.name#'"
);
```

Expand Down
6 changes: 3 additions & 3 deletions project_types/wheels/standards/wheels-cli-reference.md
Original file line number Diff line number Diff line change
Expand Up @@ -272,7 +272,7 @@ component extends="wheels.migrator.Migration" {
t.string(columnName="firstName");
t.string(columnName="lastName");
t.string(columnName="email");
t.boolean(columnName="isActive", defaultValue=true);
t.boolean(columnName="isActive", default=true);
t.timestamps(); // Adds createdAt, updatedAt
t.create();
}
Expand All @@ -291,8 +291,8 @@ component extends="wheels.migrator.Migration" {
component extends="wheels.migrator.Migration" {
function up() {
transaction {
addIndex(table="users", columns="email", unique=true);
addIndex(table="posts", columns="userId,createdAt");
addIndex(table="users", columnNames="email", unique=true);
addIndex(table="posts", columnNames="userId,createdAt");
}
}

Expand Down
28 changes: 20 additions & 8 deletions project_types/wheels/standards/wheels-model-patterns.md
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,7 @@ function config() {
### Custom Validations
```cfml
function config() {
validates(property="password", method="validatePasswordStrength");
validate(property="password", method="validatePasswordStrength");
}

private void function validatePasswordStrength() {
Expand Down Expand Up @@ -155,18 +155,30 @@ stats = model("Order").findAll(
);
```

## Scopes
## Reusable Queries

### Custom Finder Methods
In Wheels, models don’t support `scope()` like Rails.
Instead, you can define **custom finder methods** that wrap `findAll()` or `findOne()` with your common conditions.

### Named Scopes
```cfml
function config() {
scope(name="active", where="status = 'active'");
scope(name="recent", where="createdAt > #createDate()#", order="createdAt DESC");
function findActive() {
return findAll(
where = "status = 'active'"
);
}

function findRecent() {
return findAll(
where = "createdAt > #createDate()#",
order = "createdAt DESC"
);
}


// Usage
activeUsers = model("User").active().findAll();
recentOrders = model("Order").recent().findAll();
activeUsers = model("User").findActive();
recentUsers = model("User").findRecent();
```

## Data Manipulation
Expand Down