SlideShare a Scribd company logo
1 of 8
Object Validation
Wheels utilizes validation setup within the model to enforce appropriate data constraints and
persistence. Validation may be performed for saves, creates, and updates.

Basic Setup
In order to establish the full cycle of validation, 3 elements need to be in place:

   1. Model file containing business logic for the database table. Example: models/User.cfc
   2. Controller file for creating, saving or updating a model instance. Example:
       controllers/Users.cfc
   3. View file for displaying the original data inputs and an error list. Example:
       views/users/index.cfm

Note: Saving, creating, and updating model objects can also be done from the model file itself (or
even in the view file if you want to veer completely off into the wild). But to keep things simple, all
examples in this chapter will revolve around code in the controller files.

The Model
Validations are always defined in the init() method of your model. This keeps everything nice
and tidy because another developer can check init() to get a quick idea on how your model
behaves.

Let's dive right into a somewhat comprehensive example:

<cfcomponent extends="Model" output="false">

<cffunction name="init">
<cfset validatesPresenceOf(properties="firstName,lastName,email,age,password")>
<cfset validatesLengthOf(properties="firstName,lastName", maximum=50)>
<cfset validatesUniquenessOf(property="email")>
<cfset validatesNumericalityOf(property="age", onlyInteger=true)>
<cfset validatesConfirmationOf(property="password")>
</cffunction>

</cfcomponent>

This is fairly readable on its own, but this example defines the following rules that will be run
before a create, update, or save is called:

   •   The firstName, lastName, email, age, and password fields must be provided, and they
       can't be blank.
   •   At maximum, firstName and lastName can each be up to 50 characters long.
   •   The value provided for email cannot already be used in the database.
   •   The value for age can only be an integer.
   •   password must be provided twice, the second time via a field called
       passwordConfirmation.
If any of these validations fail, Wheels will not commit the create or update to the database. As
you'll see later in this chapter, the controller should check for this and react accordingly by showing
error messages generated by the model.

Listing of Validation Functions

     •    validatesConfirmationOf()
     •    validatesExclusionOf()
     •    validatesFormatOf()
     •    validatesInclusionOf()
     •    validatesLengthOf()
     •    validatesNumericalityOf()
     •    validatesPresenceOf()
     •    validatesUniquenessOf()
     •    validate()
     •    validateOnCreate()
     •    validateOnUpdate()

Use when, if, or unless to Limit the Scope of Validation

If you want to limit the scope of the validation, you have 3 arguments at your disposal: when, if,
and unless.

when     Argument

The when argument accepts 3 possible values.

     •    onSave (the   default)
     •    onCreate
     •    onUpdate

To limit our email validation to run only on create, we would change that line to this:

<cfset validatesUniquenessOf(property="email", when="onCreate")>

if   and unless Arguments

if and unless provide even more flexibility when the when argument isn't specific enough for your
validation's needs.

Each argument accepts a string containing an expression to evaluate. if specifies when the
validation should be run. unless specifies when the validation should not be run.

As an example, let's say that the model should only verify a CAPTCHA if the user is logged out,
but not when they enter their name as "Ben Forta":

<cfset validate(method="validateCaptcha", if="not isLoggedIn()",
unless="this.name is 'Ben Forta'")>

Custom Validations
At the end of the listing above are 3 custom validation functions: validate(),
validateOnCreate(), and validateOnUpdate(). These functions allow you to create your own
validation rules that aren't covered by Wheels's out-of-the-box functions.

There is only one difference between how the different functions work:

   •   validate() runs on the save event, which     happens on both create and update.
   •   validateOnCreate() runs on create.
   •   validateOnUpdate() runs on update.

To use a custom validation, we pass one of these functions a method or set of methods to run:

<cfset validate(method="validateEmailFormat")>

We then should create a method called validateEmailFormat, which in this case would verify that
the value set for this.email is in the proper format. If not, then the method sets an error message
for that field using the addError() function.

<cffunction name="validateEmailFormat" access="private">
<cfif not IsValid("email", this.email)>
<cfset addError(property="email", message="Email address is not in a valid
format.")>
</cfif>
</cffunction>

Note that IsValid() is a function build into your CFML engine.

This is a simple rule, but you can surmise that this functionality can be used to do more complex
validations as well. It's a great way to isolate complex validation rules into separate methods of your
model.

Adding Errors to the Model Object as a Whole

We've mainly focused on adding error messages at a property level, which admittedly is what you'll
be doing 80% of the time. But we can also add messages at the model object level with the
addErrorToBase() function.

As an example, here's a custom validation method that doesn't allow the user to sign up for an
account between the hours of 3:00 and 4:00 am in the server's time zone:

<cffunction name="disallowMaintenanceWindowRegistrations" access="private">
<cfset var loc = {}>
<cfset loc.hourNow = DatePart("h", Now())>

<cfif loc.hourNow gte 3 and loc.hourNow lt 4>
<cfset loc.timeZone = CreateObject("java", "java.util.TimeZone").getDefault()>
<cfset addErrorToBase(message="We're sorry, but we don't allow new registrations
between the hours of 3:00 and 4:00 am #loc.timeZone#.")>
</cfif>
</cffunction>

Sure, we could add logic to the view to also not show the registration form, but this validation in the
model would make sure that data couldn't be posted via a script between those hours as well. Better
safe than sorry if you're running a public-facing application!
The Controller
The controller continues with the simplicity of validation setup, and at the most basic level requires
only 5 lines of code to persist the form data or return to the original form page to display the list of
errors.

<cfcomponent extends="Controller" output="false">

<cffunction name="save">
<!--- User model from form fields via params --->
<cfset newUser = model("user").new(params.newUser)>

<!--- Persist new user --->
<cfif newUser.save()>
<cfset redirectTo(action="success")>
<!--- Handle errors --->
     <cfelse>
<cfset renderPage(action="index")>
</cfif>
</cffunction>

</cfcomponent>

The first line of the action creates a newUser based on the user model and the form inputs (via the
params struct).

Now, to persist the object to the database, the model's save() call can be placed within a <cfif>
test. If the save succeeds, the save() method will return true, and the contents of the <cfif> will
be executed. But if any of the validations set up in the model fail, the save() method returns false,
and the <cfelse> will execute.

The important step here is to recognize that the <cfelse> renders the original form input page
using the renderPage() function. When this happens, the view will use the newUser object defined
in our save() method. If a redirectTo() were used instead, the validation information loaded in
our save() method would be lost.

The View
Wheels factors out much of the error display code that you'll ever need. As you can see by this
quick example, it appears to mainly be a normal form. But when there are errors in the provided
model, Wheels will apply styles to the erroneous fields.

<cfoutput>

#errorMessagesFor("newUser")#

#startFormTag(action="save")#
#textField(label="First Name", objectName="newUser", property="nameFirst")#
#textField(label="Last Name", objectName="newUser", property="nameLast")#
#textField(label="Email", objectName="newUser", property="email")#
#textField(label="Age", objectName="newUser", property="age")#
#passwordField(label="Password", objectName="newUser", property="password")#
#passwordField(label="Re-type Password to Confirm", objectName="newUser",
property="passwordConfirmation")#
    #submitTag()#
#endFormTag()#

</cfoutput>

The biggest thing to note in this example is that a field called passwordConfirmation was
provided so that the validatesConfirmationOf() validation in the model can be properly tested.

For more information on how this code behaves when there is an error, refer to the Form Helpers
and Showing Errors chapter.

Error Messages
For your reference, here are the default error message formats for the different validation functions:

           Function                       Format
validatesConfirmationOf() [property] should match confirmation
  validatesExclusionOf() [property] is reserved
    validatesFormatOf()   [property] is invalid
  validatesInclusionOf() [property] is not included in the list
    validatesLengthOf()   [property] is the wrong length
validatesNumericalityOf() [property] is not a number
   validatesPresenceOf() [property] can't be empty
 validatesUniquenessOf() [property] has already been taken


Custom Error Messages

Wheels models provide a set of sensible defaults for validation errors. But sometimes you may want
to show something different than the default.

There are 2 ways to accomplish this: through global defaults in your config files or on a per-
property basis.

Setting Global Defaults for Error Messages

Using basic global defaults for the validation functions, you can set error messages in your config
file at config/settings.cfm.

<cfset set(functionName="validatesPresenceOf", message="Please provide a value
for [property]")>

As you can see, you can inject the property's name by adding [property] to the message string.
Wheels will automatically separate words based on your camelCasing of the variable names.

Setting an Error Message for a Specific Model Property

Another way of adding a custom error message is by going into an individual property in the model
and adding an argument named message.

Here's a change that we may apply in the init() method of our model:
<cfset validatesNumericalityOf(property="email", message="Email address is
already in use in another account")>
validatesLengthOf()


Description
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.


Function Syntax
validatesLengthOf([     properties, message, when, allowBlank, exactly, maximum,
minimum, within, if,    unless ])

Parameters
Parameter     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       Supply a custom error message here to override
                                 the wrong
                                                     the built-in one.
                                 length
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.
exactly      numeric   No        0                   The exact length that the property value has to
                                                     be.
maximum      numeric   No        0                   The maximum length that the property value
                                                     has to be.
minimum      numeric   No        0                   The minimum length that the property value
                                                     has to be.
within       string    No                            A list of two values (minimum and maximum)
                                                     that the length of the property value has to fall
                                                     within.
if           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).

Examples
<!--- Make sure that the `firstname` and `lastName` properties are not more than
50 characters and use square brackets to dynamically insert the property name
when the error message is displayed to the user (the `firstName` property will
be displayed as "first name") --->
<cfset validatesLengthOf(properties="firstName,lastName", maximum=50,
message="Please shorten your [property] please, 50 characters is the maximum
length allowed.")>

<!--- Make sure that the `password` property is between 4 and 15 characters --->
<cfset validatesLengthOf(property="password", within="4,20", message="The
password length has to be between 4 and

More Related Content

What's hot

Cis 407 i lab 6 of 7
Cis 407 i lab 6 of 7Cis 407 i lab 6 of 7
Cis 407 i lab 6 of 7
helpido9
 
Diving into VS 2015 Day5
Diving into VS 2015 Day5Diving into VS 2015 Day5
Diving into VS 2015 Day5
Akhil Mittal
 
Cis407 a ilab 6 web application development devry university
Cis407 a ilab 6 web application development devry universityCis407 a ilab 6 web application development devry university
Cis407 a ilab 6 web application development devry university
lhkslkdh89009
 
Ctools presentation
Ctools presentationCtools presentation
Ctools presentation
Digitaria
 

What's hot (20)

Cis 407 i lab 6 of 7
Cis 407 i lab 6 of 7Cis 407 i lab 6 of 7
Cis 407 i lab 6 of 7
 
Murach: How to transfer data from controllers
Murach: How to transfer data from controllersMurach: How to transfer data from controllers
Murach: How to transfer data from controllers
 
Javascript validating form
Javascript validating formJavascript validating form
Javascript validating form
 
AWS Lambda Hands-on: How to Create Phone Call Notifications in a Serverless Way
 AWS Lambda Hands-on: How to Create Phone Call Notifications in a Serverless Way AWS Lambda Hands-on: How to Create Phone Call Notifications in a Serverless Way
AWS Lambda Hands-on: How to Create Phone Call Notifications in a Serverless Way
 
Test Coverage for Your WP REST API Project
Test Coverage for Your WP REST API ProjectTest Coverage for Your WP REST API Project
Test Coverage for Your WP REST API Project
 
Beginner’s tutorial (part 2) how to integrate redux-saga in react native app
Beginner’s tutorial (part 2) how to integrate redux-saga in react native appBeginner’s tutorial (part 2) how to integrate redux-saga in react native app
Beginner’s tutorial (part 2) how to integrate redux-saga in react native app
 
Murach : HOW to work with controllers and routing
Murach : HOW to work with controllers and routingMurach : HOW to work with controllers and routing
Murach : HOW to work with controllers and routing
 
Murach: An introduction to web programming with ASP.NET Core MVC
Murach: An introduction to web programming with ASP.NET Core MVCMurach: An introduction to web programming with ASP.NET Core MVC
Murach: An introduction to web programming with ASP.NET Core MVC
 
Diving into VS 2015 Day5
Diving into VS 2015 Day5Diving into VS 2015 Day5
Diving into VS 2015 Day5
 
Cis407 a ilab 6 web application development devry university
Cis407 a ilab 6 web application development devry universityCis407 a ilab 6 web application development devry university
Cis407 a ilab 6 web application development devry university
 
C sharp and asp.net interview questions
C sharp and asp.net interview questionsC sharp and asp.net interview questions
C sharp and asp.net interview questions
 
OmniFaces validators
OmniFaces validatorsOmniFaces validators
OmniFaces validators
 
How to implement multiple authentication guards in laravel 8
How to implement multiple authentication guards in laravel 8How to implement multiple authentication guards in laravel 8
How to implement multiple authentication guards in laravel 8
 
Ctools presentation
Ctools presentationCtools presentation
Ctools presentation
 
Mastering OmniFaces - A Problem to Solution Approach
Mastering OmniFaces - A Problem to Solution ApproachMastering OmniFaces - A Problem to Solution Approach
Mastering OmniFaces - A Problem to Solution Approach
 
Say Goodbye to Procedural Programming - Nick Sutterer
Say Goodbye to Procedural Programming - Nick SuttererSay Goodbye to Procedural Programming - Nick Sutterer
Say Goodbye to Procedural Programming - Nick Sutterer
 
Behavioral pattern 4
Behavioral pattern 4Behavioral pattern 4
Behavioral pattern 4
 
Ivanti Cheat Sheet by Traversys Limited
Ivanti Cheat Sheet by Traversys LimitedIvanti Cheat Sheet by Traversys Limited
Ivanti Cheat Sheet by Traversys Limited
 
Testy integracyjne
Testy integracyjneTesty integracyjne
Testy integracyjne
 
Less10 2 e_testermodule_9
Less10 2 e_testermodule_9Less10 2 e_testermodule_9
Less10 2 e_testermodule_9
 

Similar to Wheels

Spring 3: What's New
Spring 3: What's NewSpring 3: What's New
Spring 3: What's New
Ted Pennings
 
Coldbox developer training – session 4
Coldbox developer training – session 4Coldbox developer training – session 4
Coldbox developer training – session 4
Billie Berzinskas
 

Similar to Wheels (20)

Asp.NET MVC
Asp.NET MVCAsp.NET MVC
Asp.NET MVC
 
Form Validation in JavaScript
Form Validation in JavaScriptForm Validation in JavaScript
Form Validation in JavaScript
 
Validation in Jakarta Struts 1.3
Validation in Jakarta Struts 1.3Validation in Jakarta Struts 1.3
Validation in Jakarta Struts 1.3
 
Mastering Assertions in Automation Testing, Importance and Best Practices.pdf
Mastering Assertions in Automation Testing, Importance and Best Practices.pdfMastering Assertions in Automation Testing, Importance and Best Practices.pdf
Mastering Assertions in Automation Testing, Importance and Best Practices.pdf
 
Spring 3: What's New
Spring 3: What's NewSpring 3: What's New
Spring 3: What's New
 
Asp.net mvc training
Asp.net mvc trainingAsp.net mvc training
Asp.net mvc training
 
Coldbox developer training – session 4
Coldbox developer training – session 4Coldbox developer training – session 4
Coldbox developer training – session 4
 
HTML::FormFu talk for Sydney PM
HTML::FormFu talk for Sydney PMHTML::FormFu talk for Sydney PM
HTML::FormFu talk for Sydney PM
 
Create Components in TomatoCMS
Create Components in TomatoCMSCreate Components in TomatoCMS
Create Components in TomatoCMS
 
Qtp day 3
Qtp day 3Qtp day 3
Qtp day 3
 
AspMVC4 start101
AspMVC4 start101AspMVC4 start101
AspMVC4 start101
 
Project1 CS
Project1 CSProject1 CS
Project1 CS
 
Data driven testing
Data driven testingData driven testing
Data driven testing
 
Test automation in Loris
Test automation in LorisTest automation in Loris
Test automation in Loris
 
Tightly coupled view (model bounded view)
Tightly coupled view (model bounded view)Tightly coupled view (model bounded view)
Tightly coupled view (model bounded view)
 
Working With The Symfony Admin Generator
Working With The Symfony Admin GeneratorWorking With The Symfony Admin Generator
Working With The Symfony Admin Generator
 
validation & regular expression chacteristics
validation & regular expression chacteristicsvalidation & regular expression chacteristics
validation & regular expression chacteristics
 
Murach: How to validate data in asp.net core mvc
Murach: How to validate data in asp.net core mvcMurach: How to validate data in asp.net core mvc
Murach: How to validate data in asp.net core mvc
 
SCWCD : Handling exceptions : CHAP : 5
SCWCD : Handling exceptions : CHAP : 5SCWCD : Handling exceptions : CHAP : 5
SCWCD : Handling exceptions : CHAP : 5
 
EWD 3 Training Course Part 11: Handling Errors in QEWD
EWD 3 Training Course Part 11: Handling Errors in QEWDEWD 3 Training Course Part 11: Handling Errors in QEWD
EWD 3 Training Course Part 11: Handling Errors in QEWD
 

Recently uploaded

Cloud Frontiers: A Deep Dive into Serverless Spatial Data and FME
Cloud Frontiers:  A Deep Dive into Serverless Spatial Data and FMECloud Frontiers:  A Deep Dive into Serverless Spatial Data and FME
Cloud Frontiers: A Deep Dive into Serverless Spatial Data and FME
Safe Software
 
Why Teams call analytics are critical to your entire business
Why Teams call analytics are critical to your entire businessWhy Teams call analytics are critical to your entire business
Why Teams call analytics are critical to your entire business
panagenda
 
+971581248768>> SAFE AND ORIGINAL ABORTION PILLS FOR SALE IN DUBAI AND ABUDHA...
+971581248768>> SAFE AND ORIGINAL ABORTION PILLS FOR SALE IN DUBAI AND ABUDHA...+971581248768>> SAFE AND ORIGINAL ABORTION PILLS FOR SALE IN DUBAI AND ABUDHA...
+971581248768>> SAFE AND ORIGINAL ABORTION PILLS FOR SALE IN DUBAI AND ABUDHA...
?#DUbAI#??##{{(☎️+971_581248768%)**%*]'#abortion pills for sale in dubai@
 

Recently uploaded (20)

Strategize a Smooth Tenant-to-tenant Migration and Copilot Takeoff
Strategize a Smooth Tenant-to-tenant Migration and Copilot TakeoffStrategize a Smooth Tenant-to-tenant Migration and Copilot Takeoff
Strategize a Smooth Tenant-to-tenant Migration and Copilot Takeoff
 
Real Time Object Detection Using Open CV
Real Time Object Detection Using Open CVReal Time Object Detection Using Open CV
Real Time Object Detection Using Open CV
 
Mastering MySQL Database Architecture: Deep Dive into MySQL Shell and MySQL R...
Mastering MySQL Database Architecture: Deep Dive into MySQL Shell and MySQL R...Mastering MySQL Database Architecture: Deep Dive into MySQL Shell and MySQL R...
Mastering MySQL Database Architecture: Deep Dive into MySQL Shell and MySQL R...
 
presentation ICT roal in 21st century education
presentation ICT roal in 21st century educationpresentation ICT roal in 21st century education
presentation ICT roal in 21st century education
 
Understanding Discord NSFW Servers A Guide for Responsible Users.pdf
Understanding Discord NSFW Servers A Guide for Responsible Users.pdfUnderstanding Discord NSFW Servers A Guide for Responsible Users.pdf
Understanding Discord NSFW Servers A Guide for Responsible Users.pdf
 
Data Cloud, More than a CDP by Matt Robison
Data Cloud, More than a CDP by Matt RobisonData Cloud, More than a CDP by Matt Robison
Data Cloud, More than a CDP by Matt Robison
 
Scaling API-first – The story of a global engineering organization
Scaling API-first – The story of a global engineering organizationScaling API-first – The story of a global engineering organization
Scaling API-first – The story of a global engineering organization
 
Cloud Frontiers: A Deep Dive into Serverless Spatial Data and FME
Cloud Frontiers:  A Deep Dive into Serverless Spatial Data and FMECloud Frontiers:  A Deep Dive into Serverless Spatial Data and FME
Cloud Frontiers: A Deep Dive into Serverless Spatial Data and FME
 
Manulife - Insurer Innovation Award 2024
Manulife - Insurer Innovation Award 2024Manulife - Insurer Innovation Award 2024
Manulife - Insurer Innovation Award 2024
 
Axa Assurance Maroc - Insurer Innovation Award 2024
Axa Assurance Maroc - Insurer Innovation Award 2024Axa Assurance Maroc - Insurer Innovation Award 2024
Axa Assurance Maroc - Insurer Innovation Award 2024
 
Powerful Google developer tools for immediate impact! (2023-24 C)
Powerful Google developer tools for immediate impact! (2023-24 C)Powerful Google developer tools for immediate impact! (2023-24 C)
Powerful Google developer tools for immediate impact! (2023-24 C)
 
🐬 The future of MySQL is Postgres 🐘
🐬  The future of MySQL is Postgres   🐘🐬  The future of MySQL is Postgres   🐘
🐬 The future of MySQL is Postgres 🐘
 
Workshop - Best of Both Worlds_ Combine KG and Vector search for enhanced R...
Workshop - Best of Both Worlds_ Combine  KG and Vector search for  enhanced R...Workshop - Best of Both Worlds_ Combine  KG and Vector search for  enhanced R...
Workshop - Best of Both Worlds_ Combine KG and Vector search for enhanced R...
 
Boost Fertility New Invention Ups Success Rates.pdf
Boost Fertility New Invention Ups Success Rates.pdfBoost Fertility New Invention Ups Success Rates.pdf
Boost Fertility New Invention Ups Success Rates.pdf
 
Why Teams call analytics are critical to your entire business
Why Teams call analytics are critical to your entire businessWhy Teams call analytics are critical to your entire business
Why Teams call analytics are critical to your entire business
 
2024: Domino Containers - The Next Step. News from the Domino Container commu...
2024: Domino Containers - The Next Step. News from the Domino Container commu...2024: Domino Containers - The Next Step. News from the Domino Container commu...
2024: Domino Containers - The Next Step. News from the Domino Container commu...
 
Bajaj Allianz Life Insurance Company - Insurer Innovation Award 2024
Bajaj Allianz Life Insurance Company - Insurer Innovation Award 2024Bajaj Allianz Life Insurance Company - Insurer Innovation Award 2024
Bajaj Allianz Life Insurance Company - Insurer Innovation Award 2024
 
Connector Corner: Accelerate revenue generation using UiPath API-centric busi...
Connector Corner: Accelerate revenue generation using UiPath API-centric busi...Connector Corner: Accelerate revenue generation using UiPath API-centric busi...
Connector Corner: Accelerate revenue generation using UiPath API-centric busi...
 
+971581248768>> SAFE AND ORIGINAL ABORTION PILLS FOR SALE IN DUBAI AND ABUDHA...
+971581248768>> SAFE AND ORIGINAL ABORTION PILLS FOR SALE IN DUBAI AND ABUDHA...+971581248768>> SAFE AND ORIGINAL ABORTION PILLS FOR SALE IN DUBAI AND ABUDHA...
+971581248768>> SAFE AND ORIGINAL ABORTION PILLS FOR SALE IN DUBAI AND ABUDHA...
 
The 7 Things I Know About Cyber Security After 25 Years | April 2024
The 7 Things I Know About Cyber Security After 25 Years | April 2024The 7 Things I Know About Cyber Security After 25 Years | April 2024
The 7 Things I Know About Cyber Security After 25 Years | April 2024
 

Wheels

  • 1. Object Validation Wheels utilizes validation setup within the model to enforce appropriate data constraints and persistence. Validation may be performed for saves, creates, and updates. Basic Setup In order to establish the full cycle of validation, 3 elements need to be in place: 1. Model file containing business logic for the database table. Example: models/User.cfc 2. Controller file for creating, saving or updating a model instance. Example: controllers/Users.cfc 3. View file for displaying the original data inputs and an error list. Example: views/users/index.cfm Note: Saving, creating, and updating model objects can also be done from the model file itself (or even in the view file if you want to veer completely off into the wild). But to keep things simple, all examples in this chapter will revolve around code in the controller files. The Model Validations are always defined in the init() method of your model. This keeps everything nice and tidy because another developer can check init() to get a quick idea on how your model behaves. Let's dive right into a somewhat comprehensive example: <cfcomponent extends="Model" output="false"> <cffunction name="init"> <cfset validatesPresenceOf(properties="firstName,lastName,email,age,password")> <cfset validatesLengthOf(properties="firstName,lastName", maximum=50)> <cfset validatesUniquenessOf(property="email")> <cfset validatesNumericalityOf(property="age", onlyInteger=true)> <cfset validatesConfirmationOf(property="password")> </cffunction> </cfcomponent> This is fairly readable on its own, but this example defines the following rules that will be run before a create, update, or save is called: • The firstName, lastName, email, age, and password fields must be provided, and they can't be blank. • At maximum, firstName and lastName can each be up to 50 characters long. • The value provided for email cannot already be used in the database. • The value for age can only be an integer. • password must be provided twice, the second time via a field called passwordConfirmation.
  • 2. If any of these validations fail, Wheels will not commit the create or update to the database. As you'll see later in this chapter, the controller should check for this and react accordingly by showing error messages generated by the model. Listing of Validation Functions • validatesConfirmationOf() • validatesExclusionOf() • validatesFormatOf() • validatesInclusionOf() • validatesLengthOf() • validatesNumericalityOf() • validatesPresenceOf() • validatesUniquenessOf() • validate() • validateOnCreate() • validateOnUpdate() Use when, if, or unless to Limit the Scope of Validation If you want to limit the scope of the validation, you have 3 arguments at your disposal: when, if, and unless. when Argument The when argument accepts 3 possible values. • onSave (the default) • onCreate • onUpdate To limit our email validation to run only on create, we would change that line to this: <cfset validatesUniquenessOf(property="email", when="onCreate")> if and unless Arguments if and unless provide even more flexibility when the when argument isn't specific enough for your validation's needs. Each argument accepts a string containing an expression to evaluate. if specifies when the validation should be run. unless specifies when the validation should not be run. As an example, let's say that the model should only verify a CAPTCHA if the user is logged out, but not when they enter their name as "Ben Forta": <cfset validate(method="validateCaptcha", if="not isLoggedIn()", unless="this.name is 'Ben Forta'")> Custom Validations
  • 3. At the end of the listing above are 3 custom validation functions: validate(), validateOnCreate(), and validateOnUpdate(). These functions allow you to create your own validation rules that aren't covered by Wheels's out-of-the-box functions. There is only one difference between how the different functions work: • validate() runs on the save event, which happens on both create and update. • validateOnCreate() runs on create. • validateOnUpdate() runs on update. To use a custom validation, we pass one of these functions a method or set of methods to run: <cfset validate(method="validateEmailFormat")> We then should create a method called validateEmailFormat, which in this case would verify that the value set for this.email is in the proper format. If not, then the method sets an error message for that field using the addError() function. <cffunction name="validateEmailFormat" access="private"> <cfif not IsValid("email", this.email)> <cfset addError(property="email", message="Email address is not in a valid format.")> </cfif> </cffunction> Note that IsValid() is a function build into your CFML engine. This is a simple rule, but you can surmise that this functionality can be used to do more complex validations as well. It's a great way to isolate complex validation rules into separate methods of your model. Adding Errors to the Model Object as a Whole We've mainly focused on adding error messages at a property level, which admittedly is what you'll be doing 80% of the time. But we can also add messages at the model object level with the addErrorToBase() function. As an example, here's a custom validation method that doesn't allow the user to sign up for an account between the hours of 3:00 and 4:00 am in the server's time zone: <cffunction name="disallowMaintenanceWindowRegistrations" access="private"> <cfset var loc = {}> <cfset loc.hourNow = DatePart("h", Now())> <cfif loc.hourNow gte 3 and loc.hourNow lt 4> <cfset loc.timeZone = CreateObject("java", "java.util.TimeZone").getDefault()> <cfset addErrorToBase(message="We're sorry, but we don't allow new registrations between the hours of 3:00 and 4:00 am #loc.timeZone#.")> </cfif> </cffunction> Sure, we could add logic to the view to also not show the registration form, but this validation in the model would make sure that data couldn't be posted via a script between those hours as well. Better safe than sorry if you're running a public-facing application!
  • 4. The Controller The controller continues with the simplicity of validation setup, and at the most basic level requires only 5 lines of code to persist the form data or return to the original form page to display the list of errors. <cfcomponent extends="Controller" output="false"> <cffunction name="save"> <!--- User model from form fields via params ---> <cfset newUser = model("user").new(params.newUser)> <!--- Persist new user ---> <cfif newUser.save()> <cfset redirectTo(action="success")> <!--- Handle errors ---> <cfelse> <cfset renderPage(action="index")> </cfif> </cffunction> </cfcomponent> The first line of the action creates a newUser based on the user model and the form inputs (via the params struct). Now, to persist the object to the database, the model's save() call can be placed within a <cfif> test. If the save succeeds, the save() method will return true, and the contents of the <cfif> will be executed. But if any of the validations set up in the model fail, the save() method returns false, and the <cfelse> will execute. The important step here is to recognize that the <cfelse> renders the original form input page using the renderPage() function. When this happens, the view will use the newUser object defined in our save() method. If a redirectTo() were used instead, the validation information loaded in our save() method would be lost. The View Wheels factors out much of the error display code that you'll ever need. As you can see by this quick example, it appears to mainly be a normal form. But when there are errors in the provided model, Wheels will apply styles to the erroneous fields. <cfoutput> #errorMessagesFor("newUser")# #startFormTag(action="save")# #textField(label="First Name", objectName="newUser", property="nameFirst")# #textField(label="Last Name", objectName="newUser", property="nameLast")# #textField(label="Email", objectName="newUser", property="email")# #textField(label="Age", objectName="newUser", property="age")# #passwordField(label="Password", objectName="newUser", property="password")# #passwordField(label="Re-type Password to Confirm", objectName="newUser", property="passwordConfirmation")# #submitTag()#
  • 5. #endFormTag()# </cfoutput> The biggest thing to note in this example is that a field called passwordConfirmation was provided so that the validatesConfirmationOf() validation in the model can be properly tested. For more information on how this code behaves when there is an error, refer to the Form Helpers and Showing Errors chapter. Error Messages For your reference, here are the default error message formats for the different validation functions: Function Format validatesConfirmationOf() [property] should match confirmation validatesExclusionOf() [property] is reserved validatesFormatOf() [property] is invalid validatesInclusionOf() [property] is not included in the list validatesLengthOf() [property] is the wrong length validatesNumericalityOf() [property] is not a number validatesPresenceOf() [property] can't be empty validatesUniquenessOf() [property] has already been taken Custom Error Messages Wheels models provide a set of sensible defaults for validation errors. But sometimes you may want to show something different than the default. There are 2 ways to accomplish this: through global defaults in your config files or on a per- property basis. Setting Global Defaults for Error Messages Using basic global defaults for the validation functions, you can set error messages in your config file at config/settings.cfm. <cfset set(functionName="validatesPresenceOf", message="Please provide a value for [property]")> As you can see, you can inject the property's name by adding [property] to the message string. Wheels will automatically separate words based on your camelCasing of the variable names. Setting an Error Message for a Specific Model Property Another way of adding a custom error message is by going into an individual property in the model and adding an argument named message. Here's a change that we may apply in the init() method of our model:
  • 6. <cfset validatesNumericalityOf(property="email", message="Email address is already in use in another account")>
  • 7. validatesLengthOf() Description 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. Function Syntax validatesLengthOf([ properties, message, when, allowBlank, exactly, maximum, minimum, within, if, unless ]) Parameters Parameter 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 Supply a custom error message here to override the wrong the built-in one. length 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. exactly numeric No 0 The exact length that the property value has to be. maximum numeric No 0 The maximum length that the property value has to be. minimum numeric No 0 The minimum length that the property value has to be. within string No A list of two values (minimum and maximum) that the length of the property value has to fall within. if 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). Examples <!--- Make sure that the `firstname` and `lastName` properties are not more than 50 characters and use square brackets to dynamically insert the property name
  • 8. when the error message is displayed to the user (the `firstName` property will be displayed as "first name") ---> <cfset validatesLengthOf(properties="firstName,lastName", maximum=50, message="Please shorten your [property] please, 50 characters is the maximum length allowed.")> <!--- Make sure that the `password` property is between 4 and 15 characters ---> <cfset validatesLengthOf(property="password", within="4,20", message="The password length has to be between 4 and