Skip to main content
Ben Nadel at CFCamp 2023 (Freising, Germany) with: Nolan Erck
Ben Nadel at CFCamp 2023 (Freising, Germany) with: Nolan Erck

OOPhoto - More Thoughts On Data Validation In Object Oriented ColdFusion Programming

By
Published in Comments (16)

The latest OOPhoto application can be experienced here.

The latest OOPhoto code can be seen here.

Before I get into the coding, I just wanted to give data validation in object oriented programming a little more thought. I don't want to get caught in an endless loop of analysis, but I think a little up front discussion is a good idea. Starting at the most low level, I want to work my way up through the various types of validation that might be required.

As the most basic level, we have to check that our instance data is of the correct type and that it has certain criteria. For example, if we are expecting an email address, we have to make sure that the value is:

  1. A string.
  2. A valid email format (ignoring those people who seem to think that having addresses with "+" in them are important :P).
  3. Is less than or equal to 70 characters in length due to the database field size.

Normally, I would do some of this data type validation as part of the CFArgument tag on my Set-style functions. However, since we are using generic getters and setters in this application we give up that ability. As such, all of this type of validation needs to be part of some sort of Validate() method, probably located within the Bean itself. What we don't want to do is drive ourselves insane with the extreme repetition of all of these type of validation. There must be a limited number of ways in which this type of validation can be done; we need to find a way to abstract that out and encapsulate it in such a way that it can easily be shared.

Dealing with composed objects is a little more gray. For example, in OOPhoto, a gallery must have at least one photo to be considered valid. Should this be a special validation case? Or is this really just be an Array that has a minimum length of one? Of course, in reality, for a photo gallery to be valid, not only does it have to have at least one photo, but each of those photos has to be valid as well.

Now that we have touched on single-object validation, let's talk about validation that involves the relationship between data points. By this, I mean something like the uniqueness of email addresses or usernames. Let's say that for a User object to be valid, it has to have a unique username within the system. Now, the User object itself has no concept of this - it exists on an island with no concept of other users. Likewise, I know that I have a unique Social Security Number because I am told that is true by a higher authority - not because I have validated that constraint myself. The validation of SSN uniqueness is not MY concern, it's the concern of the entity that ties me to other citizens - the application, if you will, within which we all reside.

To get validation of this nature, all we have to do is put some sort of validation in our service layer. This validate method will know about all the related entities and therefore can handle the relationship-based validation. This is no problem with a top level object, but when it comes to composed objects, it is slightly more complicated. For a composed object to be validated, it will have to be passed up to its own service layer by its parent object. This requires our top level objects to have pointers to the service layer of its composed objects.

Or does it? One way that we can get around this is to encapsulate that redirection behind each object's Validate() method. We could call Validate() on an object and have that turn around and call its own service layer:

<cffunction name="Validate()">
	<cfreturn VARIABLES.ServiceObject.Validate( THIS ) />
</cffunction>

This way, we can always call Validate() on an object and be certain that both data type and relational validation rules are going to be applied.

The last layer of validation has nothing to do with objects at all. The last layer of data validation involves form-specific validation. For example, making sure that the provided CAPTCHA text matches the image or making sure that a confirmation password matches the first password. Stuff like this has nothing to do with the data of the application; it is there as part of the user interface experience only. Therefore, we need something in place that handles that outside of the primary objects. I guess, what we need is an object that exists only to validate a given form.

While we're at it, why not have that form-specific object process the form as well. This would go back to what Dan Wilson was referring to in that forms should be "processed" rather than viewed by the Controller as data submissions. This way, we could keep the Controller very short and simple.

Oh my god. Something just occurred to me! All this time, I have been thinking that my validation and processing logic has been in my Controller. But it isn't. My controller just consists of CFSwitch statements! My validation and processing actually happens in my "action" files. A very interesting epiphany. I have definitely been operating under the assumption that I need to shrink my Controller, but that simply isn't true. I am not saying that the above conversation doesn't apply - it still does - but I have to stop hating on my controller so much; she's actually pretty slim already.

Sorry for that digression. Remember, I don't know any of this stuff - you are getting the raw output stream from my head :) I'm learning as I go here.

I think for starters, I will create the validation methods in my object but I will leave the form-specific validation in my action files. I am not sure about these form-specific objects. There's something about them that feels very wrong. My gut is telling me that since they are one-to-one in my application (one object per specific form) there is no need to add the overhead of an object. I might as well just leave that logic in my "action" file.

Ok, the next time we talk, I should have some new code to review.

Want to use code from this post? Check out the license.

Reader Comments

34 Comments

While I'm not doing OOP in my applications, the pages I use to process data from forms (akin to your "action" pages) call functions within a Validation singleton object for doing generic validations like the ones you mentioned at the top of the post (checking data type, checking length, etc.). Any validation logic specific to the form and the interplay of the data collected by the form is handled by the action page itself. So I definitely see similarities in what you're working on.

15,848 Comments

@Brian,

I am sitting here right now thinking of ways to get generic validation. Really, I'd like each type of validation to be its own method. But, I think I'll end up going with a IsValid() type method like CF uses:

IsValidProperty( type, value, [, criteria1, criteria2, etc.] )

I just had a cool though. Rather than IsValid style method, I could have one that is called something like:

GetValidationError()

This could return a string OR a VOID. If it returns a VOID, it could actually destroy the returning struct index automatically. That could be really useful.

<cfset errors.Field1 = GetValidationError( .... ) />
<cfset errors.Field2 = GetValidationError( .... ) />
<cfset errors.Field3 = GetValidationError( .... ) />
<cfset errors.Field4 = GetValidationError( .... ) />
<cfset errors.Field5 = GetValidationError( .... ) />

Then, only the invalid values will be left inthe errors struct. All other fields will have been valid, returned NULL, and essentially erased themselves from the errors struct.

Of course, this is not readable and really just a byproduct of the way VOID works in ColdFusion. Cool thought, though.

I was thinking of putting the generic validation methods in my Base Bean object since only beans will ever really need this type of generic validation. But, I like your Singleton idea as well. The singleton would package it up nicely into a cohesive set of functionality.

34 Comments

My validation singleton has about a dozen or so validation functions. Most just accept the data as the lone argument (isNotEmpty, isInteger, isNumeric, isPositive, isNegative, isDate, etc) while a few take additional parameters (hasRightLength, isInRange).

All of them return either true or false: none of them return any sort of feedback message. The action page takes care of determining the feedback message since the message is directly related to the context of the form.

I have found that there are times when I have form data that is NOT part of a database record or bean, but is rather form data that determines certain behaviors or decisions. Since my validation methods are in a singleton, they can be applied to form data that has nothing to do with the data within a bean.

15,848 Comments

@Brian,

I definitely agree with letting the action page translate the errors into form-contextual messages. I don't believe this should be a responsibility of the bean.

15,848 Comments

I think I have decided not to keep any validation directly in the bean object itself. Instead, I am going to have all of my beans' Validate() method turn around call the Validate() method on the corresponding service object. I just don't see a need to split up the validation between two objects especially when I can still call validate on the target object. I think it will be more maintainable to have all the validation in the Service object than to have to look in various objects depending on the type of validation.

15,848 Comments

... basically, I'm making:

Bean.Validate( Errors )

... a short hand for:

BeanService.Validate( Bean, Errors )

I think this feels right and I believe still keeps with an OOP style of thinking in which we have "smart" objects.

74 Comments

Can you do something like the jQuery validate on the Server side? This is something I've thought about a number of times. While jQuery works by using metadeta from the tag itself, it can also use a custom struct that takes a field id, then one or more validation rules and error messages for each of those validation errors.

I question whether or not the bean should even manage generic validation. I like to think that the bean should "know" about complex validation, but not "fat fingers"/lazy validation.

Your controller has the validator bean and can interact with it and then pass info to the data bean for any possible complex validation.

15,848 Comments

@Justin,

I am not familiar with the jQuery validate functionality.

Right now, I am moving my validation to the Service objects. I'll see how I feel about that when I'm done.

3 Comments

One thing that I have been experimenting with is a MetaValidation service that I call before I start setting values into the bean and then providing contextual error back to the user. Here is an example of my service...

http://blayter.com/downloads/MetaValidationService.cfc.txt

The nice part about using this is that on my edit forms I can validate that the ID of the record being edited is a valid entry by trying to go and call the other services in my application. It creates a bit of overhead on saving a form but I know that I can trust that the data is good by the time I have validated and cleaned all user entries.

132 Comments

@Ben

That's exactly how my setup works too. I have a ValidationService that generates validator objects for each object type (I'm using Transfer, so I inspect the transfer objects to figure out data types for high level validation). I also use some database introspection to get at some more specific types that transfer hides as well.

Ignoring the Transfer part it works like this:

object.setValidator( validationService.createValidator(object) );

Validators can be reused across all objects of the same type, since they have the same properties, so createValidator() returns the cached one if it already existed.

Now I can call object.validate() and it'll validate the object's properties by doing:

return getValidator().validateObject(this);

Which returns a collection of errors about types, lengths, etc. They're fairly generic and contain no HTML because that should be handled by the view when it transforms the errors later.

The properties are type safe too, because their setter and getter arguments are declared with types, so you couldn't really put "foo" where a number was expected either. This seems right to me because I'd rather not have objects in invalid states at that fundamental level, though that's kind of a style thing.

So to handle that kind of validation of arbitrary data I have a populate() method that also provides the same kind of validation, but instead validates a struct.

function populate( struct values ) {
var result = getValidator().validateValues( this, values );
if( result.isSuccess() ) {
// cfloop over values to find matching setters and call each one
}
return result;
}

result = object.populate(form);

Since you're using generic get/set, you could use cfproperty to add information about which properties were allowed to be populated. Looping over the values, and not the object also lets you populate partially like in a multi-step form.

I used cfproperty to add custom validation logic too.

<cfproperty name="OnValidate(age)" value="validateAge">

And when the createValidator() is called it inspects the metadata and then figures out what methods to call, (in this case it'd call validateAge(value) when it came across the age value in the populate), to perform the validation properly.

132 Comments

As an aside, if we wanted to go with the really conventional approach we could drop the <cfproperty name="OnValidate()"> entirely and just search for methods that match "validate" + propertyName.

I like that approach more, but also kind of liked the block of validation definitions at the top of the component when I first did this.

I think I might go with the implicit approach in the future though.

15,848 Comments

@Elliott,

That sounds very cool. For starters, I am just gonna put the validation in the services and not even worry about something clever like a validation object. I would like to, eventually, refactor to something like that, but my brain just isn't there yet.

3 Comments

@Ben

For my forms I use cf_terraform for the client and server side validation.
product: http://www.eswsoftware.com/products/terraform/index.cfm
documentation: http://www.eswsoftware.com/products/terraform/docs/formats_and_datatypes/

It has a feature of a customValidationPath in the cf_terraForm tag of where you can place your custom server side validation. In there is where I make the calls to the metaValidationService.cfc in a try - catch to see if the field is good to go. In the catch I move the error into the list that is returned to the user of what is actually wrong with the form.

While the form and custom tag are very procedural it has been solid resource to use to make sure that the data is good.

I believe in love. I believe in compassion. I believe in human rights. I believe that we can afford to give more of these gifts to the world around us because it costs us nothing to be decent and kind and understanding. And, I want you to know that when you land on this site, you are accepted for who you are, no matter how you identify, what truths you live, or whatever kind of goofy shit makes you feel alive! Rock on with your bad self!
Ben Nadel