|
Good evening everyone! I’m happy to announce that I’ve committed a
number of changes and fixes tonight. Here’s a quick rundown: 1) Jared’s
bugs from the other day related to reflexive relationships. 2) The changes
to transactions which were detailed earlier today. 3) The changes
to the oo query which stemmed from Ray’s email earlier today. 4) Big changes
related to validation and the dictionary objects which impact records. I’ll
detail these below. Validation… The reactorFactory now has a new method, createValidator().
This generates and returns a new validator object. (More on validators in a
sec) When you call createDictionary() on the reactorFactory
(which you would not typically do, but which the framework does for you) it
doesn’t generate an object, it generates an xml document. Previous
versions of reactor had the root node of the dictionary xml documents being “dictionary”.
This has changed to the alias of the object. So, if you have any
dictionary objects you will need to open each xml file and update the root node
to be the correct alias for the object or you will not get your defined
messages back. There’s a new object ErrorCollection which replaces
the ValidationErrorCollection (which had previously been stolen from
Model-Glue). In my opinion the object has a simpler
interface. Dump the object to get all the details. You can call getErrors()
to get an array of all errors. This will have elements with the structure
of {alias}.{field}.{specificError}. For example
User.firstName.notProvided. These match up to the nodes in the dictionary
object. Coincide, the error collection contains a reference to a
dictionary which can be used to automatically translate the errors.
Simply call getTranslatedErrors() on the errorCollection. There’s
also the standard hasErrors() method. When Record objects are validated the validation cascades to
dirty children. So if you have a user who hasMany addresses, when you
call user.validate() it will validate any addresses that are dirty. The validate method on the record object no longer returns
the error collection. You should remove the generated validate method
from all your custom objects. Move the logic into the custom validator objects. When you call YourRecord.validate() the record creates an
instance of the validator and passes itself to it. The resulting
ErrorCollection is placed in an instance variable in the record. This
allows for the following methods on the record: validated() – Indicates if the record has been
validated ever. hasErrors() – Indicates if the record has
errors. If the record has not been validate this will throw errors. _getErrorCollection() – returns the error
collection. If the record has not been validate this will throw errors. The project validators are somewhat complex. There’s
a base validate method. This calls methods to validate each field.
Each of those methods call methods that validate specific properties of the
field. For example, a validator for a user object might have a validate
method that calls validateFirstName and validateLastName.
ValidateFirstName would call validateFirstNameProvided and validateFirstNameLength. So, if you wanted to change how one particular property of a
field is validated you would essentially copy the method out of the project
file into your custom file and the edit it as needed. For example, in the
blog sample application the validateArticleProvided method checks to make sure
that the entry has a length. However, I want it to strip out html before
checking. This is what the custom validator looks like: <cfcomponent hint="I am the validator object for the
Entry object. I am generated, but not overwritten if I exist. You
are safe to edit me." extends="reactor.project.ReactorBlog.Validator.EntryValidator"> <!---
Place custom code here, it will not be overwritten ---> <!---
validateArticleProvided ---> <cffunction
name="validateArticleProvided" access="public" hint="I
validate that the article field was provided" output="false"
returntype="reactor.util.ErrorCollection"> <cfargument
name="EntryRecord" hint="I am the EntryRecord to validate."
required="no"
type="reactor.project.ReactorBlog.Record.EntryRecord" /> <cfargument
name="ErrorCollection" hint="I am the error collection to
populate. If not provided a new collection is created."
required="no" type="reactor.util.ErrorCollection"
default="#createErrorCollection(arguments.EntryRecord._getDictionary())#"
/> <cfset
var article = Trim(ReReplaceNoCase(arguments.EntryRecord.getArticle(),
'(<(.|\n)+?>)|&.+?;|\r|\n|\t', "", "all")) /> <!---
make sure the user actually provided an article ---> <cfif
NOT Len(article)> <cfset
arguments.ErrorCollection.addError("Entry.article.notProvided") /> </cfif> <cfreturn
arguments.ErrorCollection /> </cffunction> </cfcomponent> If you wanted to add an additional level of validation to a property
you would create your own validate method and then override the field’s
validate method. Tell it to call super.validate() and then call your
custom methods. The following is from the blog sample too. When a new
user is added it checks to make sure the username wasn’t already used: <cfcomponent hint="I am the validator object for the
User object. I am generated, but not overwritten if I exist. You
are safe to edit me." extends="reactor.project.ReactorBlog.Validator.UserValidator"> <!---
Place custom code here, it will not be overwritten ---> <!---
validateUsername ---> <cffunction
name="validateUsername" access="public" hint="I
validate the username field" output="false"
returntype="reactor.util.ErrorCollection"> <cfargument
name="UserRecord" hint="I am the UserRecord to validate."
required="no"
type="reactor.project.ReactorBlog.Record.UserRecord" /> <cfargument
name="ErrorCollection" hint="I am the error collection to
populate. If not provided a new collection is created."
required="no" type="reactor.util.ErrorCollection"
default="#createErrorCollection(arguments.UserRecord._getDictionary())#"
/> <cfset
super.validateUsername(arguments.UserRecord, arguments.ErrorCollection) /> <!---
validate username is unique ---> <cfset
validateUsernameIsUnique(arguments.UserRecord, arguments.ErrorCollection)> <cfreturn
arguments.ErrorCollection /> </cffunction> <!---
validateUsernameIsUnique ---> <cffunction
name="validateUsernameIsUnique" access="public"
hint="I validate that the username field is unique"
output="false"
returntype="reactor.util.ErrorCollection"> <cfargument
name="UserRecord" hint="I am the UserRecord to validate."
required="no"
type="reactor.project.ReactorBlog.Record.UserRecord" /> <cfargument
name="ErrorCollection" hint="I am the error collection to
populate. If not provided a new collection is created."
required="no" type="reactor.util.ErrorCollection"
default="#createErrorCollection(arguments.UserRecord._getDictionary())#"
/> <cfset
var UserGateway = _getReactorFactory().createGateway("User") /> <!---
insure that another user with the same username does not already exist ---> <cfif
UserGateway.validateUserName(arguments.UserRecord.getUserId(),
arguments.UserRecord.getUserName())> <cfset
arguments.ErrorCollection.addError("User.username.duplicateName")
/> </cfif> <cfreturn
arguments.ErrorCollection /> </cffunction> </cfcomponent> So, to avoid errors you’ll need to move all your
validation logic into validator objects and fix the dictionary objects. As always, delete your project files too! Doug |
- [Reactor For CF] Yet another big update committed Doug Hughes

