A few weeks ago, the people at CFWheels announced a contest to get people to try out CFWheels. To enter the contest all you need to do is build a version of litepost in CFWheels. If you haven’t heard of litepost, its a simple blogging app thats been used to demonstrate different ColdFusion frameworks. Really all you need from the litepost project is the database. Then just build some CRUD for the users, entries, comments, etc. I’ve been wanting to learn more about this framework for a while so I thought this contest would be a good excuse to check it out. The top 3 winners get Amazon gift cards.

So far I’ve found this to be a pretty neat framework. I see a lot of similarity to Rails. The documentation is very good. The plugins are pretty neat, you just drop a zip file into your plugins directory and you can start using that plugin.

To start I downloaded cfwheels and setup my database. I installed the scaffolding plugin and used that to generate my CRUD views, models and controllers. Already I had the basics working! I tried creating/listing/editing users and it worked great.

After setting up the scaffolding, I needed to define table relationships (called associations in CFWheels), and add some drop down menus where the scaffolding had put text fields.

One thing I noticed fairly quickly was that the crud pages were kind of ugly. I started to write some CSS to clean them up, but right away ran into trouble because the form inputs were nested inside the label tags like this:

<label for="name">Name:<input name="name"></label>

I haven’t seen it done that way before, I usually have the label completely separate from the input like this:

<label for="name">Name:</label><input name="name">

For me at least, that makes it a lot easier to style. That is also how the W3 demonstrates the tag. At first I thought this was a problem with the scaffolding plugin, but actually its CFWheels. The scaffolding plugin just writes code like this:

#textField(objectName='user', property='username', label='Username')#

So its the built in textField() method that generates the code this way. I found this is easy to fix though, you can add this additional attribute:

#textField(objectName='user', property='username', label='Username', labelPlacement='before')#

That will place the <label> tag before the form input, just how I like it. I didn’t have to go add the attribute to all by edit pages, either, you can set it in once in your settings.cfm file like this:

<cfset set(functionName="textField", labelPlacement="before")>

But, you’ll need to do it for all your inputs, like this:

<cfset set(functionName="textField", labelPlacement="before")>
<cfset set(functionName="textArea", labelPlacement="before")>
<cfset set(functionName="select", labelPlacement="before")>
etc.

Next I removed the created and updated timestamp fields from the create and edit pages. The scaffolding had put them on there but I want them to be updated automatically. CFWheels supports this, if the fields are named createdAt and updatedAt. Unfortunately the timestamp fields in the litepost database are not named like that.

After digging through the documentation a little I discovered you can tell cfwheels you are using differently named timestamp columns by putting lines like these in your settings.cfm file:

<cfset set(timeStampOnCreateProperty = "dateCreated")>
<cfset set(timeStampOnUpdateProperty = "dateUpdated")>

I tried that but had some problems. When trying to create a new entry (a blog article) I got this error:

Error Executing Database Query. Field 'dateCreated' doesn't have a default value.

It didn’t seem like my timeStampOnCreateProperty() call was working. I finally ended up renaming the fields in my database and related code.

Unfortunately, I still got an error:
Field 'updatedAt' doesn't have a default value

I was stuck on this for a while, but one of the guys behind the contest, Mike Henke was nice enough to help (several times in fact). He pointed out that the error was actually about the updatedAt column now, not about the createdAt field. Hmmm. It would make sense that updatedAt would be null when the record is first created. But looking at the database I could see that it didn’t allow nulls for the updated timestamp. I changed the database to allow nulls and tried creating an entry again. This time: Success!

Some other things I’ve gone through:

By default the textField() method will use the column name for the label. So the page ends up looking like

Fname:

I would like it to use “First Name” for the label. I found that you can specify a label using the label attribute (hey that makes sense!).

#textField(objectName='user', property='fname', label='First Name')#

I ran into a similar issue with the CFWheels validation code. Validation is done in your model file like this:

validatesPresenceOf(properties="fname,lname,email,username,password")

This makes sure those fields are not blank. If you leave fname blank for example, you’ll get this error “Fname can’t be empty”. I didn’t find any easy way to specify the label. I did find there was a way to specify a custom error message, so I was able to use that and call the validation method for every field:

<cfset validatesPresenceOf(properties="fname",message="First Name can't be empty")>
<cfset validatesPresenceOf(properties="lname",message="Last Name can't be empty")>
etc.

I thought it would be nice if I could specify just a label to use, something like this:

validatesPresenceOf(properties="fname:First Name,lname:Last Name,email,username,password")

Even better, what if you could define a column’s label in a central place? Maybe in the settings file, similar to how you set other column properties. Then it could automatically use the label everywhere, including the textField() calls. So instead of having to do this:

#textField(objectName='user', property='fname', label='First Name')#

You could just do this:

#textField(objectName='user', property='fname')#

And wheels would know what label to use for fname. This would make it really easy to change labels across the whole site!

To wrap it up, I think this is a promising framework that is gaining popularity. I’ve even seen some job postings mentioning cfwheels experience desired. I’ve done a lot of work with Model Glue, and I find it to be very heavy on configuration. I’d like to see some changes to MG to pick up some of the great things from CFWheels.

6 Comments

  1. Raul Riera says:

    That seems like a bug in my plugin.. I will look right into it 🙂

    Glad to have you on board testing the framework

  2. Raul Riera says:

    Were the columns named createdAt, updatedAt or deletedAt? if so, Scaffold SHOULDN'T place those form helpers in your views

  3. Ryan says:

    Raul: no, my columns were not named createdAt/updatedAt when I ran the scaffolding plugin.     I don't think your plugin did anything wrong.

  4. Raul Riera says:

    Phew 🙂

  5. Chris Peters says:

    Thanks for the honest account, Ryan!

    One suggestion that I can offer is to rely more on the "mapping" part of "object relational mapping" to get your data references in plainer English. Then you may end up fighting less with Wheels defaults like error messages.

    You can use the property() method on your model to define that "fname" should be mapped to "firstName". Then Wheels will treat the column like a real adult.

    Try this out in init():
    <cfset property(name="firstName", column="fname")>
    <cfset property(name="lastName", column="lname")>
    etc…

    Good luck with the contest. I'm glad to hear that there's someone out there working on it!

    P.S. If you either create a category or tag in WordPress for "ColdFusion on Wheels," "CFWheels," or whatever, I can add your Wheels-related posts to our Wheels Bloggers aggregator on cfwheels.org. It gives your posts a little extra exposure from others interested in the framework.

  6. Brian says:

    Great write up – thanks Ryan.