Persisting Complex Objects
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]
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]
The ProductRecord.save() method actually uses the ProductDAO.create() to do it’s work, but at at that point you have a lot more control over the life of the record than when you’re using the DAO directly.
Multiple Objects?
This is something I’m still trying to find a good solution to, but Model-Glue Unity offers one at least. To expand on this example, assume that each product also belongs to any number of categories. These are managed through a mapping table, so product has and belongs to many categories. Per linked to a good example of how this is done in Rails on the CFWheels google group, although it is surprisingly more code than the Model Glue way. How do they do it? Well, automagically.
Model-Glue 2: Unity has this concept of Generic events. You’ve probably seen them if you’ve ever tried scaffolding, or watching one of the videos on Model Glue. The idea is that you’ll create a small tag in your controller and it’ll perform all the work for you. For example, to insert a Product, complete with all related categories, your entire controller code might be as simple as:
<message name="ModelGlue.genericCommit"> <argument name="recordName" value="productRecord" /> <argument name="criteria" value="productID" /> <argument name="object" value="product" /> <argument name="validationName" value="productValidation" /> </message>
This is very abbreviated code for all this work. Basically this is telling ModelGlue to try to commit to the product table of the database, and if productID was passed in from the form it should instead an update, if no productID is passed in it will be an insert. If there’s a problem creating the product we’ll save the validation errors to productValidation. That’s quite a lot of information, but how does ModelGlue know to commit this? Well it’s all based on Reactor configuration of your object along with the naming of your form fields.
Getting the form setup isn’t difficult, but takes forethought that you’re going to use the genericCommit action rather than do the work yourself. Form field names on your form should match the database columns declared in the product table. Mapping those to be inserted is helpful, but how does it add all the categories? Assume in this case you have a list of checkboxes for your categories, where you check off whatever categories you want this product to be a part of. It would be important to name this checkbox category|categoryID if you want to it to work. This is assuming you have a category object in reactor as well as a link table that will contain just categoryID and productID. That’s actually all you need for this to be inserted/updated at that point. If you’re updating you’ll want a hidden field with the productID of course, but other than that.
So how does this work? Well that’s opening a can of beans. Joe’s done an excellent job of creating helpful additions to the framework so fast that I haven’t seen people post each feature. The real magic for this occurs in the modelglue.unity.orm.ReactorAdapter.cfc file in the assemble() method. This method will get all the metadata from reactor for the table you’re working with, in this case product, and loop over it. The basic population is achieved with the makeEventBean() bean function which is extremely useful whether you’re using GenericCommits or not, but the magic for these related tables also exists here. The ReactorAdapter loops over all fields from the metadata structure and attempts to populate any that have a plural relationship with the product object. In the end this assemble() method will delete any records from the mapping table that weren’t passed in and add any new children to there. The whole Generic set of functions is one of the most impressive things about Model-Glue, and enables some extremely fast development if you know what you’re doing.
What about doing something like this in a more service oriented architecture though? What is the framework knew enough based on your form names to create tiered structure containing everything — the product and the categories. Basically I’m trying to find a service oriented approach to creating objects without hardcoding the relationships like categories as is done in the ModelGlue example. Basically the code would look something like this:
[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.
