Jason asks:

Brian,

I'd like to ask a question about Beans and Validation. Feel free to use it is a blog post.

So a lot of people have been telling me that Beans could/should have a validate() method to validate their own data.

My issue with this is, how can a bean validate it's own data if it is throwing errors in get() methods because you are trying to insert invalid data.

Since I cannot insert "Jason" into a date var or a numeric var, what good is my bean's validate() method gonna do me.

It seems to me that I have two choices.

1. I can do pre-validation validation. And validate the data before I insert it into the bean for validation

2. I can do all of my validation in the service layer or handler/listener(if no service layer) and THEN populate my bean with valid data.

I like option 2.

Am I making sense?

FYI, I am an OO n00b.

Hi Jason, don't worry, you're making sense. I actually don't do either of these. Before I do anything like a save() of an object, I create a Result object. My Result is a generic object with methods like isSuccess(), hasErrors(), getErrors(), etc. I use this to handle my validation.

Basically, I call user.populate(data, result). The populate function attempts to populate the object by calling setters that correspond to the data that was passed. (For customization, an object can override it's inherited populate() method if the generic behavior isn't sufficient.) If there are any failures, an error is caught and added to the Result object.

Once that is done, attempt to save the object even if there are errors, so that I can get additional validation error messages (i.e. end date must be after start date). Saving is done the same way: user.save(result). The save() call triggers a validation, and any validation errors not based on type are added to the Result as well. If the Result contains no errors after validation, I proceed with the save. If there are errors, the save does not continue.

Once that is done, I have a Result object that either has errors or it doesn't. These may be population errors cause by type mismatches, or they may be validation errors handled by my validation rules. Regardless, I now have a nicely packaged Result object that I can return to the calling code. This can be returned to something like Flex, or it can be returned to an HTML Controller such as Model-Glue, but either way, that code can now decide how to proceed.

This is just the way I do things but I've found it to be very flexible and useful. I hope that helps!

Comments Comments (6) | del.ico.us del.icio.us | Digg It! Digg It! | Linking Blogs Linking Blogs | 5775 Views

Comments

  • # Posted By Bob Silverberg | 7/16/08 12:01 AM

    Very nice, Brian. I've been struggling with just this issue - what to do about invalid data that cannot be "set" into the object. One of the wrinkles is that I need to save the info somehow to redisplay to the user. I have my own way of "forcing" it into the object anyway, but your idea of a Result object is appealing. There's no reason why I couldn't put those values into the result, whence I'd retrieve them to display to the user.

    Thanks for sharing.

  • # Posted By Jason Dean | 7/16/08 12:22 AM

    Thanks for the response to my question. I like the idea of having the populate methods do the data type checking, but Bob also bring up a great point about re-displaying the values (even the bad ones) to the users.

    One of my other questions has been, how to do this. I have had a few people tell me that they base their views on Beans/Transfer objects, but, how do we redisplay the bad data to the user if it can't be set into the bean/TrO?

    What I have been doing is basing my views on the Event Object/Request Context (ColdBox) and using its getters to render my view. Then, if I were to use your methodology, I think I would pass the form fields to the populate() method which would try to populate the bean, if it could not it would return errors. At this point I would not even try to force anything into the bean or call save() I would add the error messages to the event object and pass it onto the form again for the user to correct.

    Once populate() returned zero errors, I would know it is OK to call save().

    What do you think?

  • # Posted By Peter Bell | 7/16/08 12:01 PM

    Hi Brian,

    Interesting. How do you handle the more general situation where there may not be a single population event. For instance, you might load some data from a from into a bean, but you might also calculate and then set some other properties, so rather than a single population event there may be multiple population/set events? Obviously a work around would be to load up everything into a single struct and then load that into the bean - is that the approach you take?

    To date I've basically kept my setters untyped, allowing invalid data to be entered into the bean and then I just validate the data within the bean when I want to call isValid() or isValid<state/role>(). Any thoughts on relative strengths/weaknesses of the two approaches?

  • # Posted By Jason Dean | 7/16/08 1:42 PM

    @Peter - I love the idea of keeping setters untyped. Every time I have come into issues with using Beans for forms, validation, etc it has been because I cannot insert invalid data into a bean and then validate it with a different method.

    Unfortunately, I also want to use Transfer for my Business Objects, but I don't think this is an options, so I need to pre-validate my data before I populate. However I am considering using the custom populate method in a Transfer Decorator to validate during population and returning a list of errors, as Brian suggested.

  • # Posted By Brian Kotek | 7/16/08 3:01 PM

    @Jason - I prefer to get as many validation errors as I can at once. If you check population and save/validation separately, you run the risk of forcing the user through 2 "fix these errors" screens.

    @Peter - Yeah, I haven't had a situation where my population of the new or updated data for the object doesn't happen in one action.

  • # Posted By Richard | 7/19/08 6:27 AM

    Brian,

    This one has me pulling my hair out for quite a while...

    I like having validation in the beans (in my case TransferDecorators that have a validate method) so to guarantee that the beans can stand up for themselves and do not have to rely on the service layer or whatever (why I want this? I don't know).

    So this is what I did after reading your post. Validate() first tries to populate all fields. Data type errors are caught and saved in an error struct, so basically the same way you suggest.

    The error struct's keys are the fieldnames (NAME, AGE etc). It's values consist of an array containing a struct with two fields: errortype and value (input that caused the error).

    Additional validation (enddate > startdate) also goes in validate() and consequent validation errors are added to the corresponding field's error array.

    Validation errors that cannot be attributed to one specific field will be added to a key 'general'. Now there's enough information to pass to the user, in the view I can still decide how much detail to show.

    Furthermore my validate method has an optional argument ignorefieldsList, containing a list of fields to ignore (you bet) on validation, which can be handy when the form is part of a wizard (multi step form).

    Alas, this does not solve the issue of re-displaying the wrong entries. When returned to the form, form fields 'recover' themselves from bad data, that can however still be displayed in a message line, since they do reside in the error struct. But then the bean does accept data that is nonetheless not validated, but has the right data type. This false validated input does show up in the formfields, because the bean has accepted it. That makes for an inconsistent UI treatment of non valid input.