Building search forms with tableless models in CFWheels
In this post, I hope to persuade you that you will rarely ever need the
Tag-based form helpers (
selectTag, etc.) in your CFWheels apps ever again.
“How?” you ask.
The answer: through the use of a wonderful feature that we affectionately call tableless models.
How you’re probably used to coding search forms in CFWheels
So let’s code up an index form using the
Tag-based helpers that you’re probably accustomed to using in this situation. The view’s job is to display a list of
invoice records along with a form for narrowing by start date and end date:
This is pretty common, and I wouldn’t go as far to say that it’s wrong.
Let’s code what we need in the controller to wire everything up.
But wait! We can’t have a
startDate that occurs after the
endDate. We better add a check for that in the controller:
index action is getting pretty beefy at this point. And now we’re starting to validate our data in the controller, which can quickly turn into a tangled mess after we’ve added another field or two to the form.
Cleaning up the search form with tableless models
As it turns out, models in CFWheels come with a bunch of really helpful methods for validating data. And even though we’re not using this form to save data to a database, we can still use the model validations to validate our data. Hooray!
All that we need to do is create a CFC in our
models folder that represents this particular form. The initializer will contain a call to
table(false), which tells CFWheels to not try to connect it to a database.
In addition to
table(false), we can call all of the model validation initializers that we need to validate the data.
Lastly, we need to create a method that validates data passed into the model and runs the query if all is well.
Here is the finished product in
endDate become properties on the model in the
this scope. This allows us to validate those properties and refer to them in an object-oriented manner.
run method is called, there will be a
results property set on the object containing the search query.
Next, we rewire the controller to this much simpler form:
Much cleaner, huh? Excluding whitespace and comments, this reduces the contents of the
index action from 16 lines of actual code to 5.
This methodology also improves the view because we can now use
textField instead of
textFieldTag, and we can display validation errors near the affected form fields:
As a bonus, our
InvoiceSearchForm model allows us to do things like set the labels/error message labels on the form fields using
property, and allows us to do most of what models allow us to do: namely validations and callbacks.
I find this to be a nice pattern because it ties the form to the model in a fairly clean, object-oriented way: the model represents the form, so it makes sense for it to define how labels on the form should appear.
Other uses for tableless models
I plan on writing more blog posts that incorporate this methology. Here are some other ideas where tableless models are a Good Idea™:
- Authentication forms
- The model takes care of all authentication logic.
- Password change/reset forms
- Move interface-based concepts like
validatesConfirmationOfout of the table-based
usermodel and into a tableless model.
- Database transactions involving multiple models
- Nested properties have their limits and logic related to them can really pollute your table-based models. Handle all of the logic in a model that’s intimately involved with the form.
- Have you ever found yourself in a situation where you needed to run a query involving multiple database tables, but it was unclear which model to write the query in? Tableless models are a perfect way to avoid making a random decision.
- NoSQL and API integration
- Are you saving your data somewhere other than a relational database? You can still model the business logic using CFWheels models.
Also, major kudos to Tony Petruzzi for adding this awesome feature into CFWheels, which made its way into the v1.3 release.