PageRenderTime 230ms CodeModel.GetById 101ms app.highlight 8ms RepoModel.GetById 113ms app.codeStats 0ms

/wheels/model/validations.cfm

http://cfwheels.googlecode.com/
ColdFusion | 630 lines | 594 code | 24 blank | 12 comment | 94 complexity | 0929fb9b08b6e76fbd00123644100456 MD5 | raw file
  1<!--- PUBLIC MODEL INITIALIZATION METHODS --->
  2
  3<!--- high level validation helpers --->
  4
  5<cffunction name="validatesConfirmationOf" returntype="void" access="public" output="false" hint="Validates that the value of the specified property also has an identical confirmation value. (This is common when having a user type in their email address a second time to confirm, confirming a password by typing it a second time, etc.) The confirmation value only exists temporarily and never gets saved to the database. By convention, the confirmation property has to be named the same as the property with ""Confirmation"" appended at the end. Using the password example, to confirm our `password` property, we would create a property called `passwordConfirmation`."
  6	examples=
  7	'
  8		<!--- Make sure that the user has to confirm their password correctly the first time they register (usually done by typing it again in a second form field) --->
  9		<cfset validatesConfirmationOf(property="password", when="onCreate", message="Your password and its confirmation do not match. Please try again.")>
 10	'
 11	categories="model-initialization,validations" chapters="object-validation" functions="validatesExclusionOf,validatesFormatOf,validatesInclusionOf,validatesLengthOf,validatesNumericalityOf,validatesPresenceOf,validatesUniquenessOf">
 12	<cfargument name="properties" type="string" required="false" default="" hint="Name of property or list of property names to validate against (can also be called with the `property` argument).">
 13	<cfargument name="message" type="string" required="false" hint="Supply a custom error message here to override the built-in one.">
 14	<cfargument name="when" type="string" required="false" default="onSave" hint="Pass in `onCreate` or `onUpdate` to limit when this validation occurs (by default validation will occur on both create and update, i.e. `onSave`).">
 15	<cfargument name="condition" type="string" required="false" default="" hint="String expression to be evaluated that decides if validation will be run (if the expression returns `true` validation will run).">
 16	<cfargument name="unless" type="string" required="false" default="" hint="String expression to be evaluated that decides if validation will be run (if the expression returns `false` validation will run).">
 17	<cfif StructKeyExists(arguments, "if")>
 18		<cfset arguments.condition = arguments.if>
 19		<cfset StructDelete(arguments, "if")>
 20	</cfif>
 21	<cfset $args(name="validatesConfirmationOf", args=arguments)>
 22	<cfset $registerValidation(methods="$validatesConfirmationOf", argumentCollection=arguments)>
 23</cffunction>
 24
 25<cffunction name="validatesExclusionOf" returntype="void" access="public" output="false" hint="Validates that the value of the specified property does not exist in the supplied list."
 26	examples=
 27	'
 28		<!--- Do not allow "PHP" or "Fortran" to be saved to the database as a cool language --->
 29		<cfset validatesExclusionOf(property="coolLanguage", list="php,fortran", message="Haha, you can not be serious. Try again, please.")>
 30	'
 31	categories="model-initialization,validations" chapters="object-validation" functions="validatesConfirmationOf,validatesExclusionOf,validatesFormatOf,validatesInclusionOf,validatesLengthOf,validatesNumericalityOf,validatesPresenceOf,validatesUniquenessOf">
 32	<cfargument name="properties" type="string" required="false" default="" hint="See documentation for @validatesConfirmationOf.">
 33	<cfargument name="list" type="string" required="true" hint="Single value or list of values that should not be allowed.">
 34	<cfargument name="message" type="string" required="false" hint="See documentation for @validatesConfirmationOf.">
 35	<cfargument name="when" type="string" required="false" default="onSave" hint="See documentation for @validatesConfirmationOf.">
 36	<cfargument name="allowBlank" type="boolean" required="false" hint="If set to `true`, validation will be skipped if the property value is an empty string or doesn't exist at all. This is useful if you only want to run this validation after it passes the @validatesPresenceOf test, thus avoiding duplicate error messages if it doesn't.">
 37	<cfargument name="condition" type="string" required="false" default="" hint="See documentation for @validatesConfirmationOf.">
 38	<cfargument name="unless" type="string" required="false" default="" hint="See documentation for @validatesConfirmationOf.">
 39	<cfif StructKeyExists(arguments, "if")>
 40		<cfset arguments.condition = arguments.if>
 41		<cfset StructDelete(arguments, "if")>
 42	</cfif>
 43	<cfscript>
 44		$args(name="validatesExclusionOf", args=arguments);
 45		arguments.list = $listClean(arguments.list);
 46		$registerValidation(methods="$validatesExclusionOf", argumentCollection=arguments);
 47	</cfscript>
 48</cffunction>
 49
 50<cffunction name="validatesFormatOf" returntype="void" access="public" output="false" hint="Validates that the value of the specified property is formatted correctly by matching it against a regular expression using the `regEx` argument and/or against a built-in CFML validation type using the `type` argument (`creditcard`, `date`, `email`, etc.)."
 51	examples=
 52	'
 53		<!--- Make sure that the user has entered a correct credit card --->
 54		<cfset validatesFormatOf(property="cc", type="creditcard")>
 55
 56		<!--- Make sure that the user has entered an email address ending with the `.se` domain when the `ipCheck()` method returns `true`, and it''s not Sunday. Also supply a custom error message that overrides the Wheels default one --->
 57		<cfset validatesFormatOf(property="email", regEx="^.*@.*\.se$", condition="ipCheck()", unless="DayOfWeek() IS 1", message="Sorry, you must have a Swedish email address to use this website.")>
 58	'
 59	categories="model-initialization,validations" chapters="object-validation" functions="validatesConfirmationOf,validatesExclusionOf,validatesInclusionOf,validatesLengthOf,validatesNumericalityOf,validatesPresenceOf,validatesUniquenessOf">
 60	<cfargument name="properties" type="string" required="false" default="" hint="See documentation for @validatesConfirmationOf.">
 61	<cfargument name="regEx" type="string" required="false" default="" hint="Regular expression to verify against.">
 62	<cfargument name="type" type="string" required="false" default="" hint="One of the following types to verify against: `creditcard`, `date`, `email`, `eurodate`, `guid`, `social_security_number`, `ssn`, `telephone`, `time`, `URL`, `USdate`, `UUID`, `variableName`, `zipcode` (will be passed through to your CFML engine's `IsValid()` function).">
 63	<cfargument name="message" type="string" required="false" hint="See documentation for @validatesConfirmationOf.">
 64	<cfargument name="when" type="string" required="false" default="onSave" hint="See documentation for @validatesConfirmationOf.">
 65	<cfargument name="allowBlank" type="boolean" required="false" hint="See documentation for @validatesExclusionOf.">
 66	<cfargument name="condition" type="string" required="false" default="" hint="See documentation for @validatesConfirmationOf.">
 67	<cfargument name="unless" type="string" required="false" default="" hint="See documentation for @validatesConfirmationOf.">
 68	<cfif StructKeyExists(arguments, "if")>
 69		<cfset arguments.condition = arguments.if>
 70		<cfset StructDelete(arguments, "if")>
 71	</cfif>
 72	<cfscript>
 73		$args(name="validatesFormatOf", args=arguments);
 74		if (application.wheels.showErrorInformation)
 75		{
 76			if (Len(arguments.type) && !ListFindNoCase("creditcard,date,email,eurodate,guid,social_security_number,ssn,telephone,time,URL,USdate,UUID,variableName,zipcode", arguments.type))
 77				$throw(type="Wheels.IncorrectArguments", message="The `#arguments.type#` type is not supported.", extendedInfo="Use one of the supported types: `creditcard`, `date`, `email`, `eurodate`, `guid`, `social_security_number`, `ssn`, `telephone`, `time`, `URL`, `USdate`, `UUID`, `variableName`, `zipcode`");
 78		}
 79		$registerValidation(methods="$validatesFormatOf", argumentCollection=arguments);
 80	</cfscript>
 81</cffunction>
 82
 83<cffunction name="validatesInclusionOf" returntype="void" access="public" output="false" hint="Validates that the value of the specified property exists in the supplied list."
 84	examples=
 85	'
 86		<!--- Make sure that the user selects either "Wheels" or "Rails" as their framework --->
 87		<cfset validatesInclusionOf(property="frameworkOfChoice", list="wheels,rails", message="Please try again, and this time, select a decent framework!")>
 88	'
 89	categories="model-initialization,validations" chapters="object-validation" functions="validatesConfirmationOf,validatesExclusionOf,validatesFormatOf,validatesLengthOf,validatesNumericalityOf,validatesPresenceOf,validatesUniquenessOf">
 90	<cfargument name="properties" type="string" required="false" default="" hint="See documentation for @validatesConfirmationOf.">
 91	<cfargument name="list" type="string" required="true" hint="List of allowed values.">
 92	<cfargument name="message" type="string" required="false" hint="See documentation for @validatesConfirmationOf.">
 93	<cfargument name="when" type="string" required="false" default="onSave" hint="See documentation for @validatesConfirmationOf.">
 94	<cfargument name="allowBlank" type="boolean" required="false" hint="See documentation for @validatesExclusionOf.">
 95	<cfargument name="condition" type="string" required="false" default="" hint="See documentation for @validatesConfirmationOf.">
 96	<cfargument name="unless" type="string" required="false" default="" hint="See documentation for @validatesConfirmationOf.">
 97	<cfif StructKeyExists(arguments, "if")>
 98		<cfset arguments.condition = arguments.if>
 99		<cfset StructDelete(arguments, "if")>
100	</cfif>
101	<cfscript>
102		$args(name="validatesInclusionOf", args=arguments);
103		arguments.list = $listClean(arguments.list);
104		$registerValidation(methods="$validatesInclusionOf", argumentCollection=arguments);
105	</cfscript>
106</cffunction>
107
108<cffunction name="validatesLengthOf" returntype="void" access="public" output="false" hint="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."
109	examples=
110	'
111		<!--- 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".) --->
112		<cfset validatesLengthOf(properties="firstName,lastName", maximum=50, message="Please shorten your [property] please. 50 characters is the maximum length allowed.")>
113
114		<!--- Make sure that the `password` property is between 4 and 15 characters --->
115		<cfset validatesLengthOf(property="password", within="4,20", message="The password length must be between 4 and 20 characters.")>
116	'
117	categories="model-initialization,validations" chapters="object-validation" functions="validatesConfirmationOf,validatesExclusionOf,validatesFormatOf,validatesInclusionOf,validatesNumericalityOf,validatesPresenceOf,validatesUniquenessOf">
118	<cfargument name="properties" type="string" required="false" default="" hint="See documentation for @validatesConfirmationOf.">
119	<cfargument name="message" type="string" required="false" hint="See documentation for @validatesConfirmationOf.">
120	<cfargument name="when" type="string" required="false" default="onSave" hint="See documentation for @validatesConfirmationOf.">
121	<cfargument name="allowBlank" type="boolean" required="false" hint="See documentation for @validatesExclusionOf.">
122	<cfargument name="exactly" type="numeric" required="false" hint="The exact length that the property value must be.">
123	<cfargument name="maximum" type="numeric" required="false" hint="The maximum length that the property value can be.">
124	<cfargument name="minimum" type="numeric" required="false" hint="The minimum length that the property value can be.">
125	<cfargument name="within" type="string" required="false" hint="A list of two values (minimum and maximum) that the length of the property value must fall within.">
126	<cfargument name="condition" type="string" required="false" default="" hint="See documentation for @validatesConfirmationOf.">
127	<cfargument name="unless" type="string" required="false" default="" hint="See documentation for @validatesConfirmationOf.">
128	<cfif StructKeyExists(arguments, "if")>
129		<cfset arguments.condition = arguments.if>
130		<cfset StructDelete(arguments, "if")>
131	</cfif>
132	<cfscript>
133		$args(name="validatesLengthOf", args=arguments);
134		if (Len(arguments.within))
135			arguments.within = $listClean(list=arguments.within, returnAs="array");
136		$registerValidation(methods="$validatesLengthOf", argumentCollection=arguments);
137	</cfscript>
138</cffunction>
139
140<cffunction name="validatesNumericalityOf" returntype="void" access="public" output="false" hint="Validates that the value of the specified property is numeric."
141	examples=
142	'
143		<!--- Make sure that the score is a number with no decimals but only when a score is supplied. (Tetting `allowBlank` to `true` means that objects are allowed to be saved without scores, typically resulting in `NULL` values being inserted in the database table) --->
144		<cfset validatesNumericalityOf(property="score", onlyInteger=true, allowBlank=true, message="Please enter a correct score.")>
145	'
146	categories="model-initialization,validations" chapters="object-validation" functions="validatesConfirmationOf,validatesExclusionOf,validatesFormatOf,validatesInclusionOf,validatesLengthOf,validatesPresenceOf,validatesUniquenessOf">
147	<cfargument name="properties" type="string" required="false" default="" hint="See documentation for @validatesConfirmationOf.">
148	<cfargument name="message" type="string" required="false" hint="See documentation for @validatesConfirmationOf.">
149	<cfargument name="when" type="string" required="false" default="onSave" hint="See documentation for @validatesConfirmationOf.">
150	<cfargument name="allowBlank" type="boolean" required="false" hint="See documentation for @validatesExclusionOf.">
151	<cfargument name="onlyInteger" type="boolean" required="false" hint="Specifies whether the property value must be an integer.">
152	<cfargument name="condition" type="string" required="false" default="" hint="See documentation for @validatesConfirmationOf.">
153	<cfargument name="unless" type="string" required="false" default="" hint="See documentation for @validatesConfirmationOf.">
154	<cfargument name="odd" type="boolean" required="false" hint="Specifies whether or not the value must be an odd number.">
155	<cfargument name="even" type="boolean" required="false" hint="Specifies whether or not the value must be an even number.">
156	<cfargument name="greaterThan" type="numeric" required="false" hint="Specifies whether or not the value must be greater than the supplied value.">
157	<cfargument name="greaterThanOrEqualTo" type="numeric" required="false" hint="Specifies whether or not the value must be greater than or equal the supplied value.">
158	<cfargument name="equalTo" type="numeric" required="false" hint="Specifies whether or not the value must be equal to the supplied value.">
159	<cfargument name="lessThan" type="numeric" required="false" hint="Specifies whether or not the value must be less than the supplied value.">
160	<cfargument name="lessThanOrEqualTo" type="numeric" required="false" hint="Specifies whether or not the value must be less than or equal the supplied value.">
161	<cfif StructKeyExists(arguments, "if")>
162		<cfset arguments.condition = arguments.if>
163		<cfset StructDelete(arguments, "if")>
164	</cfif>
165	<cfset $args(name="validatesNumericalityOf", args=arguments)>
166	<cfset $registerValidation(methods="$validatesNumericalityOf", argumentCollection=arguments)>
167</cffunction>
168
169<cffunction name="validatesPresenceOf" returntype="void" access="public" output="false" hint="Validates that the specified property exists and that its value is not blank."
170	examples=
171	'
172		<!--- Make sure that the user data can not be saved to the database without the `emailAddress` property. (It must exist and not be an empty string) --->
173		<cfset validatesPresenceOf("emailAddress")>
174	'
175	categories="model-initialization,validations" chapters="object-validation" functions="validatesConfirmationOf,validatesExclusionOf,validatesFormatOf,validatesInclusionOf,validatesLengthOf,validatesNumericalityOf,validatesUniquenessOf">
176	<cfargument name="properties" type="string" required="false" default="" hint="See documentation for @validatesConfirmationOf.">
177	<cfargument name="message" type="string" required="false" hint="See documentation for @validatesConfirmationOf.">
178	<cfargument name="when" type="string" required="false" default="onSave" hint="See documentation for @validatesConfirmationOf.">
179	<cfargument name="condition" type="string" required="false" default="" hint="See documentation for @validatesConfirmationOf.">
180	<cfargument name="unless" type="string" required="false" default="" hint="See documentation for @validatesConfirmationOf.">
181	<cfif StructKeyExists(arguments, "if")>
182		<cfset arguments.condition = arguments.if>
183		<cfset StructDelete(arguments, "if")>
184	</cfif>
185	<cfset $args(name="validatesPresenceOf", args=arguments)>
186	<cfset $registerValidation(methods="$validatesPresenceOf", argumentCollection=arguments)>
187</cffunction>
188
189<cffunction name="validatesUniquenessOf" returntype="void" access="public" output="false" hint="Validates that the value of the specified property is unique in the database table. Useful for ensuring that two users can't sign up to a website with identical screen names for example. When a new record is created, a check is made to make sure that no record already exists in the database with the given value for the specified property. When the record is updated, the same check is made but disregarding the record itself."
190	examples=
191	'
192		<!--- Make sure that two users with the same screen name won''t ever exist in the database (although to be 100% safe, you should consider using database locking as well) --->
193		<cfset validatesUniquenessOf(property="username", message="Sorry, that username is already taken.")>
194
195		<!--- Same as above but allow identical user names as long as they belong to a different account --->
196		<cfset validatesUniquenessOf(property="username", scope="accountId")>
197	'
198	categories="model-initialization,validations" chapters="object-validation" functions="validatesConfirmationOf,validatesExclusionOf,validatesFormatOf,validatesInclusionOf,validatesLengthOf,validatesNumericalityOf,validatesPresenceOf">
199	<cfargument name="properties" type="string" required="false" default="" hint="See documentation for @validatesConfirmationOf.">
200	<cfargument name="message" type="string" required="false" hint="See documentation for @validatesConfirmationOf.">
201	<cfargument name="when" type="string" required="false" default="onSave" hint="See documentation for @validatesConfirmationOf.">
202	<cfargument name="allowBlank" type="boolean" required="false" hint="See documentation for @validatesExclusionOf.">
203	<cfargument name="scope" type="string" required="false" default="" hint="One or more properties by which to limit the scope of the uniqueness constraint.">
204	<cfargument name="condition" type="string" required="false" default="" hint="See documentation for @validatesConfirmationOf.">
205	<cfargument name="unless" type="string" required="false" default="" hint="See documentation for @validatesConfirmationOf.">
206	<cfif StructKeyExists(arguments, "if")>
207		<cfset arguments.condition = arguments.if>
208		<cfset StructDelete(arguments, "if")>
209	</cfif>
210	<cfscript>
211		$args(name="validatesUniquenessOf", args=arguments);
212		arguments.scope = $listClean(arguments.scope);
213		$registerValidation(methods="$validatesUniquenessOf", argumentCollection=arguments);
214	</cfscript>
215</cffunction>
216
217<!--- low level validation --->
218
219<cffunction name="validate" returntype="void" access="public" output="false" hint="Registers method(s) that should be called to validate objects before they are saved."
220	examples=
221	'
222		<cffunction name="init">
223			<!--- Register the `checkPhoneNumber` method below to be called to validate objects before they are saved --->
224			<cfset validate("checkPhoneNumber")>
225		</cffunction>
226
227		<cffunction name="checkPhoneNumber">
228			<!--- Make sure area code is `614` --->
229			<cfreturn Left(this.phoneNumber, 3) is "614">
230		</cffunction>
231	'
232	categories="model-initialization,validations" chapters="object-validation" functions="validateOnCreate,validateOnUpdate">
233	<cfargument name="methods" type="string" required="false" default="" hint="Method name or list of method names to call. (Can also be called with the `method` argument.)">
234	<cfargument name="condition" type="string" required="false" default="" hint="See documentation for @validatesConfirmationOf.">
235	<cfargument name="unless" type="string" required="false" default="" hint="See documentation for @validatesConfirmationOf.">
236	<cfargument name="when" type="string" required="false" default="onSave" hint="See documentation for @validatesConfirmationOf.">
237	<cfif StructKeyExists(arguments, "if")>
238		<cfset arguments.condition = arguments.if>
239		<cfset StructDelete(arguments, "if")>
240	</cfif>
241	<cfset $registerValidation(argumentCollection=arguments)>
242</cffunction>
243
244<cffunction name="validateOnCreate" returntype="void" access="public" output="false" hint="Registers method(s) that should be called to validate new objects before they are inserted."
245	examples=
246	'
247		<cffunction name="init">
248			<!--- Register the `checkPhoneNumber` method below to be called to validate new objects before they are inserted --->
249			<cfset validateOnCreate("checkPhoneNumber")>
250		</cffunction>
251
252		<cffunction name="checkPhoneNumber">
253			<!--- Make sure area code is `614` --->
254			<cfreturn Left(this.phoneNumber, 3) is "614">
255		</cffunction>
256	'
257	categories="model-initialization,validations" chapters="object-validation" functions="validate,validateOnUpdate">
258	<cfargument name="methods" type="string" required="false" default="" hint="See documentation for @validate.">
259	<cfargument name="condition" type="string" required="false" default="" hint="See documentation for @validatesConfirmationOf.">
260	<cfargument name="unless" type="string" required="false" default="" hint="See documentation for @validatesConfirmationOf.">
261	<cfif StructKeyExists(arguments, "if")>
262		<cfset arguments.condition = arguments.if>
263		<cfset StructDelete(arguments, "if")>
264	</cfif>
265	<cfset $registerValidation(when="onCreate", argumentCollection=arguments)>
266</cffunction>
267
268<cffunction name="validateOnUpdate" returntype="void" access="public" output="false" hint="Registers method(s) that should be called to validate existing objects before they are updated."
269	examples=
270	'
271		<cffunction name="init">
272			<!--- Register the `check` method below to be called to validate existing objects before they are updated --->
273			<cfset validateOnUpdate("checkPhoneNumber")>
274		</cffunction>
275
276		<cffunction name="checkPhoneNumber">
277			<!--- Make sure area code is `614` --->
278			<cfreturn Left(this.phoneNumber, 3) is "614">
279		</cffunction>
280	'
281	categories="model-initialization,validations" chapters="object-validation" functions="validate,validateOnCreate">
282	<cfargument name="methods" type="string" required="false" default="" hint="See documentation for @validate.">
283	<cfargument name="condition" type="string" required="false" default="" hint="See documentation for @validatesConfirmationOf.">
284	<cfargument name="unless" type="string" required="false" default="" hint="See documentation for @validatesConfirmationOf.">
285	<cfif StructKeyExists(arguments, "if")>
286		<cfset arguments.condition = arguments.if>
287		<cfset StructDelete(arguments, "if")>
288	</cfif>
289	<cfset $registerValidation(when="onUpdate", argumentCollection=arguments)>
290</cffunction>
291
292<!--- PUBLIC MODEL OBJECT METHODS --->
293
294<cffunction name="valid" returntype="boolean" access="public" output="false" hint="Runs the validation on the object and returns `true` if it passes it. Wheels will run the validation process automatically whenever an object is saved to the database, but sometimes it's useful to be able to run this method to see if the object is valid without saving it to the database."
295	examples=
296	'
297		<!--- Check if a user is valid before proceeding with execution --->
298		<cfset user = model("user").new(params.user)>
299		<cfif user.valid()>
300			<!--- Do something here --->
301		</cfif>
302	'
303	categories="model-object,errors" chapters="object-validation" functions="">
304	<cfargument name="callbacks" type="boolean" required="false" default="true" hint="See documentation for @save.">
305	<cfscript>
306		var loc = {};
307		loc.returnValue = false;
308		clearErrors();
309		if ($callback("beforeValidation", arguments.callbacks))
310		{
311			if (isNew())
312			{
313				if ($callback("beforeValidationOnCreate", arguments.callbacks) && $validate("onSave,onCreate") && $callback("afterValidation", arguments.callbacks) && $callback("afterValidationOnCreate", arguments.callbacks))
314					loc.returnValue = true;
315			}
316			else
317			{
318				if ($callback("beforeValidationOnUpdate", arguments.callbacks) && $validate("onSave,onUpdate") && $callback("afterValidation", arguments.callbacks) && $callback("afterValidationOnUpdate", arguments.callbacks))
319					loc.returnValue = true;
320			}
321		}
322	</cfscript>
323	<cfreturn loc.returnValue>
324</cffunction>
325
326<cffunction name="automaticValidations" returntype="void" access="public" output="false" hint="Whether or not to enable default validations for this model."
327	examples='
328		<!--- In `models/User.cfc`, disable automatic validations. In this case, automatic validations are probably enabled globally, but we want to disable just for this model --->
329		<cffunction name="init">
330			<cfset automaticValidations(false)>
331		</cffunction>
332	'
333	categories="model-initialization,validations" chapters="object-validation" functions="">
334	<cfargument name="value" type="boolean" required="true">
335	<cfset variables.wheels.class.automaticValidations = arguments.value>
336</cffunction>
337
338<!--- PRIVATE MODEL INITIALIZATION METHODS --->
339
340<cffunction name="$registerValidation" returntype="void" access="public" output="false" hint="Called from the high level validation helpers to register the validation in the class struct of the model.">
341	<cfargument name="when" type="string" required="true">
342	<cfscript>
343		var loc = {};
344
345		// combine `method`/`methods` and `property`/`properties` into one variables for easier processing below
346		$combineArguments(args=arguments, combine="methods,method", required=true);
347		// `validate`, `validateOnCreate` and `validateOnUpdate` do not take the properties argument
348		// however other validations do.
349		$combineArguments(args=arguments, combine="properties,property", required=false);
350
351		if (application.wheels.showErrorInformation)
352		{
353			if (StructKeyExists(arguments, "properties"))
354			{
355				if (!Len(arguments.properties))
356					$throw(type="Wheels.IncorrectArguments", message="The `property` or `properties` argument is required but was not passed in.", extendedInfo="Please pass in the names of the properties you want to validate. Use either the `property` argument (for a single property) or the `properties` argument (for a list of properties) to do this.");
357			}
358		}
359
360		// loop through all methods and properties and add info for each to the `class` struct
361		loc.iEnd = ListLen(arguments.methods);
362		for (loc.i=1; loc.i <= loc.iEnd; loc.i++)
363		{
364			// only loop once by default (will be used on the lower level validation helpers that do not take arguments: `validate`, `validateOnCreate` and `validateOnUpdate`)
365			loc.jEnd = 1;
366			if (StructKeyExists(arguments, "properties"))
367				loc.jEnd = ListLen(arguments.properties);
368
369			for (loc.j=1; loc.j <= loc.jEnd; loc.j++)
370			{
371				loc.validation = {};
372				loc.validation.method = Trim(ListGetAt(arguments.methods, loc.i));
373				loc.validation.args = Duplicate(arguments);
374				if (StructKeyExists(arguments, "properties"))
375				{
376					loc.validation.args.property = Trim(ListGetAt(loc.validation.args.properties, loc.j));
377					//loc.validation.args.message = $validationErrorMessage(message=loc.validation.args.message, property=loc.validation.args.property);
378				}
379				StructDelete(loc.validation.args, "when");
380				StructDelete(loc.validation.args, "methods");
381				StructDelete(loc.validation.args, "properties");
382				ArrayAppend(variables.wheels.class.validations[arguments.when], loc.validation);
383			}
384		}
385	</cfscript>
386</cffunction>
387
388<cffunction name="$validationErrorMessage" returntype="string" access="public" output="false" hint="Creates nicer looking error text by humanizing the property name and capitalizing it when appropriate.">
389	<cfargument name="property" type="string" required="true">
390	<cfargument name="message" type="string" required="true">
391	<cfscript>
392		var returnValue = "";
393		// turn property names into lower cased words
394		returnValue = Replace(arguments.message, "[property]", LCase(this.$label(arguments.property)), "all");
395		// capitalize the first word in the property name if it comes first in the sentence
396		if (Left(arguments.message, 10) == "[property]")
397			returnValue = capitalize(returnValue);
398	</cfscript>
399	<cfreturn returnValue>
400</cffunction>
401
402<!--- PRIVATE MODEL OBJECT METHODS --->
403
404<cffunction name="$validate" returntype="boolean" access="public" output="false" hint="Runs all the validation methods setup on the object and adds errors as it finds them. Returns `true` if no errors were added, `false` otherwise.">
405	<cfargument name="type" type="string" required="true">
406	<cfargument name="execute" type="boolean" required="false" default="true">
407	<cfscript>
408		var loc = {};
409
410		// don't run any validations when we want to skip
411		if (!arguments.execute)
412			return true;
413
414		// loop over the passed in types
415		for (loc.typeIndex=1; loc.typeIndex <= ListLen(arguments.type); loc.typeIndex++)
416		{
417			loc.type = ListGetAt(arguments.type, loc.typeIndex);
418			// loop through all validations for passed in type (`onSave`, `onCreate` etc) that has been set on this model object
419			loc.iEnd = ArrayLen(variables.wheels.class.validations[loc.type]);
420			for (loc.i=1; loc.i <= loc.iEnd; loc.i++)
421			{
422				loc.thisValidation = variables.wheels.class.validations[loc.type][loc.i];
423				if ($evaluateCondition(argumentCollection=loc.thisValidation.args))
424				{
425					if (loc.thisValidation.method == "$validatesPresenceOf")
426					{
427						$validatesPresenceOf(argumentCollection=loc.thisValidation.args);
428					}
429					else
430					{
431						// if the validation set does not allow blank values we can set an error right away, otherwise we call a method to run the actual check
432						if (StructKeyExists(loc.thisValidation.args, "property") && StructKeyExists(loc.thisValidation.args, "allowBlank") && !loc.thisValidation.args.allowBlank && (!StructKeyExists(this, loc.thisValidation.args.property) || (!Len(this[loc.thisValidation.args.property]) && loc.thisValidation.method != "$validatesUniquenessOf")))
433							addError(property=loc.thisValidation.args.property, message=$validationErrorMessage(loc.thisValidation.args.property, loc.thisValidation.args.message));
434						else if (!StructKeyExists(loc.thisValidation.args, "property") || (StructKeyExists(this, loc.thisValidation.args.property) &&(Len(this[loc.thisValidation.args.property]) || loc.thisValidation.method == "$validatesUniquenessOf")))
435							$invoke(method=loc.thisValidation.method, invokeArgs=loc.thisValidation.args);
436					}
437				}
438			}
439		}
440		// now that we have run all the validation checks we can return `true` if no errors exist on the object, `false` otherwise
441		loc.returnValue = !hasErrors();
442	</cfscript>
443	<cfreturn loc.returnValue>
444</cffunction>
445
446<cffunction name="$evaluateCondition" returntype="boolean" access="public" output="false" hint="Evaluates the condition to determine if the validation should be executed.">
447	<cfscript>
448		var returnValue = false;
449		// proceed with validation when `condition` has been supplied and it evaluates to `true` or when `unless` has been supplied and it evaluates to `false`
450		// if both `condition` and `unless` have been supplied though, they both need to be evaluated correctly (`true`/`false` that is) for validation to proceed
451		if (
452			(!StructKeyExists(arguments, "condition") || !Len(arguments.condition) || Evaluate(arguments.condition))
453			&& (!StructKeyExists(arguments, "unless") || !Len(arguments.unless) || !Evaluate(arguments.unless))
454		){
455			returnValue = true;
456		}
457	</cfscript>
458	<cfreturn returnValue>
459</cffunction>
460
461<cffunction name="$validatesConfirmationOf" returntype="void" access="public" output="false" hint="Adds an error if the object property fail to pass the validation setup in the @validatesConfirmationOf method.">
462	<cfscript>
463		var virtualConfirmProperty = arguments.property & "Confirmation";
464		if (StructKeyExists(this, virtualConfirmProperty) && this[arguments.property] != this[virtualConfirmProperty])
465		{
466			addError(property=virtualConfirmProperty, message=$validationErrorMessage(arguments.property, arguments.message));
467		}
468	</cfscript>
469</cffunction>
470
471<cffunction name="$validatesExclusionOf" returntype="void" access="public" output="false" hint="Adds an error if the object property fail to pass the validation setup in the @validatesExclusionOf method.">
472	<cfscript>
473		if (ListFindNoCase(arguments.list, this[arguments.property]))
474		{
475			addError(property=arguments.property, message=$validationErrorMessage(arguments.property, arguments.message));
476		}
477	</cfscript>
478</cffunction>
479
480<cffunction name="$validatesFormatOf" returntype="void" access="public" output="false" hint="Adds an error if the object property fail to pass the validation setup in the @validatesFormatOf method.">
481	<cfscript>
482		if (
483			(Len(arguments.regEx) && !REFindNoCase(arguments.regEx, this[arguments.property]))
484			|| (Len(arguments.type) && !IsValid(arguments.type, this[arguments.property]))
485		){
486			addError(property=arguments.property, message=$validationErrorMessage(arguments.property, arguments.message));
487		}
488	</cfscript>
489</cffunction>
490
491<cffunction name="$validatesInclusionOf" returntype="void" access="public" output="false" hint="Adds an error if the object property fail to pass the validation setup in the @validatesInclusionOf method.">
492	<cfscript>
493		if (!ListFindNoCase(arguments.list, this[arguments.property]))
494		{
495			addError(property=arguments.property, message=$validationErrorMessage(arguments.property, arguments.message));
496		}
497	</cfscript>
498</cffunction>
499
500<cffunction name="$validatesPresenceOf" returntype="void" access="public" output="false" hint="Adds an error if the object property fail to pass the validation setup in the @validatesPresenceOf method.">
501	<cfargument name="property" type="string" required="true">
502	<cfargument name="message" type="string" required="true">
503	<cfargument name="properties" type="struct" required="false" default="#this.properties()#">
504	<cfscript>
505		// if the property does not exist or if it's blank we add an error on the object
506		if (
507			!StructKeyExists(arguments.properties, arguments.property)
508			|| (IsSimpleValue(arguments.properties[arguments.property]) && !Len(Trim(arguments.properties[arguments.property])))
509			|| (IsStruct(arguments.properties[arguments.property]) && !StructCount(arguments.properties[arguments.property]))
510		){
511			addError(property=arguments.property, message=$validationErrorMessage(arguments.property, arguments.message));
512		}
513	</cfscript>
514</cffunction>
515
516<cffunction name="$validatesLengthOf" returntype="void" access="public" output="false" hint="Adds an error if the object property fail to pass the validation setup in the @validatesLengthOf method.">
517	<cfargument name="property" type="string" required="true">
518	<cfargument name="message" type="string" required="true">
519	<cfargument name="exactly" type="numeric" required="true">
520	<cfargument name="maximum" type="numeric" required="true">
521	<cfargument name="minimum" type="numeric" required="true">
522	<cfargument name="within" type="any" required="true">
523	<cfargument name="properties" type="struct" required="false" default="#this.properties()#">
524	<cfscript>
525		var _lenValue = Len(arguments.properties[arguments.property]);
526
527		// for within, just create minimum/maximum values
528		if (IsArray(arguments.within) && ArrayLen(arguments.within) eq 2)
529		{
530			arguments.minimum = arguments.within[1];
531			arguments.maximum = arguments.within[2];
532		}
533
534		if(
535			(arguments.maximum && _lenValue gt arguments.maximum)
536			|| (arguments.minimum && _lenValue lt arguments.minimum)
537			|| (arguments.exactly && _lenValue != arguments.exactly)
538		){
539			addError(property=arguments.property, message=$validationErrorMessage(arguments.property, arguments.message));
540		}
541	</cfscript>
542</cffunction>
543
544<cffunction name="$validatesNumericalityOf" returntype="void" access="public" output="false" hint="Adds an error if the object property fail to pass the validation setup in the @validatesNumericalityOf method.">
545	<cfscript>
546		if (
547			!IsNumeric(this[arguments.property])
548			|| (arguments.onlyInteger && Round(this[arguments.property]) != this[arguments.property])
549			|| (IsNumeric(arguments.greaterThan) && this[arguments.property] lte arguments.greaterThan)
550			|| (IsNumeric(arguments.greaterThanOrEqualTo) && this[arguments.property] lt arguments.greaterThanOrEqualTo)
551			|| (IsNumeric(arguments.equalTo) && this[arguments.property] neq arguments.equalTo)
552			|| (IsNumeric(arguments.lessThan) && this[arguments.property] gte arguments.lessThan)
553			|| (IsNumeric(arguments.lessThanOrEqualTo) && this[arguments.property] gt arguments.lessThanOrEqualTo)
554			|| (IsBoolean(arguments.odd) && arguments.odd && !BitAnd(this[arguments.property], 1))
555			|| (IsBoolean(arguments.even) && arguments.even && BitAnd(this[arguments.property], 1))
556		){
557			addError(property=arguments.property, message=$validationErrorMessage(arguments.property, arguments.message));
558		}
559	</cfscript>
560</cffunction>
561
562<cffunction name="$validatesUniquenessOf" returntype="void" access="public" output="false" hint="Adds an error if the object property fail to pass the validation setup in the @validatesUniquenessOf method.">
563	<cfargument name="property" type="string" required="true">
564	<cfargument name="message" type="string" required="true">
565	<cfargument name="scope" type="string" required="false" default="">
566	<cfargument name="properties" type="struct" required="false" default="#this.properties()#">
567	<cfscript>
568		var loc = {};
569		loc.where = [];
570
571		// create the WHERE clause to be used in the query that checks if an identical value already exists
572		// wrap value in single quotes unless it's numeric
573		// example: "userName='Joe'"
574		ArrayAppend(loc.where, "#arguments.property#=#variables.wheels.class.adapter.$quoteValue(this[arguments.property])#");
575
576		// add scopes to the WHERE clause if passed in, this means that checks for other properties are done in the WHERE clause as well
577		// example: "userName='Joe'" becomes "userName='Joe' AND account=1" if scope is "account" for example
578		arguments.scope = $listClean(arguments.scope);
579		if (Len(arguments.scope))
580		{
581			loc.iEnd = ListLen(arguments.scope);
582			for (loc.i=1; loc.i <= loc.iEnd; loc.i++)
583			{
584				loc.property = ListGetAt(arguments.scope, loc.i);
585				ArrayAppend(loc.where, "#loc.property#=#variables.wheels.class.adapter.$quoteValue(this[loc.property])#");
586			}
587		}
588
589		if (variables.wheels.class.softDeletion)
590		{
591			ArrayAppend(loc.where, "#tableName()#.#$softDeleteColumn()# IS NULL");
592		}
593
594		// try to fetch existing object from the database
595		loc.existingObject = findOne(where=ArrayToList(loc.where, " AND "), reload=true);
596
597		// we add an error if an object was found in the database and the current object is either not saved yet or not the same as the one in the database
598		if (IsObject(loc.existingObject) && (isNew() || loc.existingObject.key() != key($persisted=true)))
599		{
600			addError(property=arguments.property, message=$validationErrorMessage(arguments.property, arguments.message));
601		}
602	</cfscript>
603</cffunction>
604
605<cffunction name="$validationExists" returntype="boolean" access="public" output="false" hint="Checks to see if a validation has been created for a property.">
606	<cfargument name="property" type="string" required="true">
607	<cfargument name="validation" type="string" required="true">
608	<cfscript>
609		var loc = {};
610		loc.returnValue = false;
611
612		for (loc.when in variables.wheels.class.validations)
613		{
614			if (StructKeyExists(variables.wheels.class.validations, loc.when))
615			{
616				loc.eventArray = variables.wheels.class.validations[loc.when];
617				loc.iEnd = ArrayLen(loc.eventArray);
618				for (loc.i = 1; loc.i lte loc.iEnd; loc.i++)
619				{
620					if (StructKeyExists(loc.eventArray[loc.i].args, "property") && loc.eventArray[loc.i].args.property == arguments.property and loc.eventArray[loc.i].method == "$#arguments.validation#")
621					{
622						loc.returnValue = true;
623						break;
624					}
625				}
626			}
627		}
628	</cfscript>
629	<cfreturn loc.returnValue />
630</cffunction>