PageRenderTime 31ms CodeModel.GetById 22ms RepoModel.GetById 0ms app.codeStats 0ms

/workspace/default/wheels/model/validations.cfm

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