yeomanly

So you want to create a CFWheels application? (Part 5)

2009 August 11
tags: CFWheels · ColdFusion
by Mike Henke

This CFWheels series is heavy borrowed from Dan Wilson's "So You Want to" series about Model Glue:Unity and matches to this post

Previously in this series, we installed CFWheels, discussed some concepts in CFWheels, added our basic flow and navigation, created add and list functionality, and validation to our Contact-O-Matic Application. Next in the series, we will cover the built in ORM of CFWheels.

An Object Relation Mapper (ORM) is used to translate between our Relational Database and our Object Oriented programming. One nice thing, it is simplifies common sql statements thus saving us time and reducing sql errors. More complex SQL will still need to be coded. Don't think you won't have to learn SQL, the ORM can't do everything better but it will make the mundane tasks simplier.

CFWheel ORM

I glossed over some important convention before in our series. CFWheels expects the table name to be plural like contacts, a primary keys in the tables, and the primary key column to be name id. Our Contacts table meets all these. We can override the table name and primary key column name but CFWheels does need a primary key.

Database Code

Lets drop our old table and create it along with a types table using this code.

DROP TABLE IF EXISTS contactomatic.contacts;CREATE TABLE 'contactomatic'.'contacts' ( 'id' INTEGER UNSIGNED NOT NULL AUTO_INCREMENT, 'name' VARCHAR(<span class="cc_numeric">45) NOT NULL, 'typeid' INTEGER UNSIGNED NOT NULL, PRIMARY KEY ('id') ) ENGINE = InnoDB;

DROP TABLE IF EXISTS contactomatic.tbl_typesofcontact; CREATE TABLE 'contactomatic'.'tbl_typesofcontact' ( 'typeid' INTEGER UNSIGNED NOT NULL AUTO_INCREMENT, 'title_for_type_of_contact' VARCHAR(<span class="cc_numeric">45) NOT NULL, PRIMARY KEY ('typeid') ) ENGINE = InnoDB;

INSERT INTO contactomatic.tbl_typesofcontact (title_for_type_of_contact) VALUES('Friend');

INSERT INTO contactomatic.tbl_typesofcontact (title_for_type_of_contact) VALUES('Enemy');

INSERT INTO contactomatic.tbl_typesofcontact (title_for_type_of_contact) VALUES('Co-Worker');

Types Model

In our /controller/contact.cfc add this code to the new action.

<cfset types = model("type").findAll() />

While we are in this file, replace the code in the list action with this:

<cfset allContacts = model("contact").findAll(include="type") /><cfdump var="#allContacts#"><cfabort>

And now lets check our add form.

URL Rewriting On = http://localhost/contact/new?reload=true

URL Rewriting Partial = http://localhost/index.cfm/contact/new?reload=true

URL Rewriting Off = http://localhost/index.cfm?controller=contact&action=new

Table Naming

Well, you can see CFWheels assumes we have a types table since our newly created model cfc's name is type. Our table's name is actually tbl_typesofcontact, we could rename the cfc to tbl_typesofcontact.cfc, but CFWheels convention also says our table name should be plural. Lets tell (configure) CFWheels we already have a table name that doesn't fit in CFWheel's conventions.

Add this code to our type.cfc in the models folder.

<cffunction name="init"><cfset table("tbl_typesofcontact") /><cfset property(name="title", column="title_for_type_of_contact") /></cffunction>

ÿ

As you can see, we tell CFWheels our table's actual name. Also we give the long column name, title_for_type_of_contact, a shorter name, title. See how easy it is to override CFWheel's table conventions. It would have been easier if initially our table was named contacttypes and the model cfc contacttype.cfc but sometimes this won't be an option.

Reload our page and you should now see our old form but look @ our debug information

Notice we have a new query. This was created by the code, <cfset types = model("type").findAll() />, we added to our new action in /controllers/contact.cfc.

Select Helper

Lets add our select box populated from this query to our form using a CFWheels helper.

Replace in /view/new.cfm

<span class="cc_normaltag"><div>#textField(objectName="newContact", property="type", label="Type")#<span class="cc_normaltag"></div>

With

<span class="cc_normaltag"><div>#select(objectName="newContact", property="typeid", options=types, label="Type", includeBlank="")#<span class="cc_normaltag"></div>

The select helper takes our blank new object, the typeid column (property), the types object, and we add a label of Type. For more information on Helper Forms, look through the code in \wheels\view\forms.cfm.

Load the page again, and now you should see the select dop down box. Lets submit the form.

Weird, we selected a valid contact type from our new select drop down box, but our validation is catching it. This is because the value the form is submitting is actually the typeid not the title.

Well, lets change our validation and add an association to mark typeid as a foreign key.

In our model/contact.cfc change the code to match this.

<cfcomponent extends="Model" output="false"><cffunction name="init"><cfset validatesPresenceOf(property="name",message="Name is Required") /><cfset validatesPresenceOf(property="typeid",message="Type of Contact is Required") /><cfset validatesUniquenessOf(property="name", message="Name is already present") /><cfset belongsTo(name="type", foreignKey="typeid")></cffunction></cfcomponent>

We basically made sure the typeid is present instead of type and declared a datebase relationship (association). Following CFWheels convention, we could use <cfset belongTo("type")> if we followed our CFWheels naming convention for the primary key of a table but instead of id, we have typeid so we need to tell CFWheels.

Lets try to submit the form again.

We are making progress, looks like the form submitted and executed our cfdump and cfabort in /controllers/contact.cfc

Association

Here is where our association we set in /models/contact.cfc comes to play. CFWheels ORM joins the tbl_typesofcontact table with the contacts table. This happens because we added the association declaring the foriegn key in our /models/contacts.cfc and in creating the query, we told it to include type. <cfset allContacts = model("contact").findAll(include="type") />

Remove <cfdump var="#allContacts#"><cfabort> from /controllers/contact.cfc in the list action and reload our list.

URL Rewriting On = http://localhost/contact/list?reload=true

URL Rewriting Partial = http://localhost/index.cfm/contact/list?reload=true

URL Rewriting Off = http://localhost/index.cfm?controller=contact&action=list&reload=true

More on Associations

We did alot of work with the built in ORM. You did a good job. Next post, we will cover the return differences between some built in ORM calls and the logic why, how to specify the return columns, specify the where clause, and create advance queries. Hopefully after that, we will cover editing and deleting records.

So you want to create a CFWheels application? (Part 5)

3 Responses leave one →
  1. reinhard
    reinhard PERMALINK
    Aug 12, 2009 at 8:08 AM

    great articles! i really enjoyed reading them and followed along.

    one question: what would in need to do if i want a contact that belongs to two catgeories, say private and company?

  1. Mike Henke
    Aug 12, 2009 at 8:41 AM

    The database would need to be changed a little to handle multiple contact types better but through the form, it is simple.

    I would probably use the checkbox helper or keep the select helper while adding the attribute multiple="true". Check the forms.cfm in the cfwheels/view folder for a list of all the form helpers.

    Also, I re-read and proof the series after the initial posts so check back for addition clarifications :-)

  1. Daniel
    Daniel PERMALINK
    Feb 26, 2010 at 5:54 PM

    Ok... in order to get things to work properly with this part of the tutorial I had to fix the relations between the two tables that are being used. I kept getting errors on the list action because the contact struct didn't contain any types. Even in the photo from Mike's query result you can see that there is a column named typeid then another redundant column named typetypeid. What I had to do was go into the types table and rename the primary key column id, which is convention in wheels, then I removed the foreignid from the belongsTo method in models/Contact.cfc. Then I added <code>&lt;cfset hasMany("contacts") /&gt;</code> to the init function in models/Type.cfc. Lastly, I modified views/contact/list.cfm so that instead of <code>&lt;td&gt;#allContacts.type#&lt;td&gt;</code> being used to list the types <code>&lt;td&gt;#allContacts.title#&lt;td&gt;</code> was used instead. After that, everything worked correctly as of the end of this part of the tutorial.

Leave a Reply

Leave this field empty: