Persisting Complex Objects

Published February 14, 2007 on adamfortuna

    When it comes to programming pages, it's almost always harder to create a page that accepts input (a form page), than a page that outputs (general dsp page). When displaying information from a database for instance, you may need to go the database and get everything you need while in the controller, then in the view display all that in some nice form. When you're accepting input though, there can be quite a few more steps. If they're editing something you'll need to do the same amount of work in your controller, plus all the logic to actually saves the record when it's submitted in addition to the form which must be prefilled with the current record. Usually when this form is submitted it's going to be more difficult to save the record back than it was to get it in the first place as well. So if we're boiling this down to units of work for a display page, we might have 1u of work in the controller to get the record and 1u to display it in the view. For the form page though you'll have those 2u + another 5u+ in the controller to param everything passed in, create the objects and then save them and forward the user to somewhere. If there are errors that's another case to handle. The actual form to display this information now needs that initial data, additional form markup, additional validation markup and possibly results ("Object updated successfully”). All that being said you're looking at 2 units of work for output, and a minimum 8 units for a form. It's no surprise that making these 8 easier to work with is something that saves developers huge amounts of time. How can these be made easier for us though? In Coldfusion there are a number of ways to handle something like this, but some things are just hard to find. Lets say you're creating a form for adding and editing products. Products will primarily exist in one table at this point (we'll go into more later). So how do you save this? Using Reactor you have two possibilities (probably many more, but two main ones come to mind). 1) When the user submits the page you could create a Product transfer object (denoted with "to” which is really just a structure) that corresponds to the table structure you're inserting into (or as you have the table defined in Reactor). Then you pass this structure into the ProductDAO.create() method which will perform the actual insert. The downside of this method is that no validation is done in this situation. create() returns void, so unless there's a caught error you won't know anything is wrong. To paraphrase, your code might look something like this (assume no mvc framework, but coldspring/reactor). [cfm] [/cfm] This example has no validation, although you could validate these with cfparam and catch the Validation exception, but that doesn't take into account any business rules you have. For instance you might want all descriptions to be at least 150 characters and all product names to be more than 3 and less than 50 characters. Although you can define the maximums in your varchar fields, defining the rules and checking before you get to the database is much more of a drag, and should really be done in your business layer, not your controller. 2) When the user submits the page you create a ProductRecord object instead of a structure. In reactors there are heavyweight objects that contain everything you need to work with a ProductRecord. You can set fields on it, see if it validates and then save the record to the database. For instance, you could do something like this instead: [cfm] [/cfm] This would then do a deep load of the object into the product object. Then you could call your default product.save(), product.validate(), product.hasErrors() methods. The downside is I don't think something like this would be plausible, as then your error collection would have to recurse through all the objects that were edited as well. I'm sure there's a better solution than that 8u example. ModelGlue2 gets it down to 5u by abstracting the validation, paraming and related objects though, so it's definitely work looking into if you're using ModelGlue.