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 ColdFusion Mailing List -- [email protected] -- Archives at http://www.mail-archive.com/reactor%40doughughes.net/

Reply via email to