RSS2 & iCal for RoomBooking System

Since Jan 2014, RSS2 and iCal have been added – these are a bit experimental – would appreciate feedback!

Each user can have a unique key generated for them by an administrator (it’s not there by default);
Once an API key is available for that user, and their role allows it, they should be able to access /api/ which will display a list of available feeds.

Feeds are ‘All’ (i.e all locations) and then split down by location, so you could have RSS for a specific location if you wanted.

Note that after creating an API key for that user, the user will need to login/out again to be able to use it, but should then see “Data Feeds” as a menu option on the Events drop down. Once they’ve got the URL with the token, that will then bypass the authentication, so you can get read access to the upcoming events.

Things learned from creating the RoomBooking System (part 2)

The Room Booking system is now available as v 1.01; the main addition being permissions, roles and authentication. As before, I thought it might be useful to highlight a few of the thought processes behind this update, and look at some of the problems come across when beginning to think about an application which is due for distribution in all sorts of environments.

Authentication varies massively depending on the setup – here are a few common scenarios:

Using authentication external to the application, i.e at a IIS/Apache level:

  1. Block the whole website: e.g, have everything behind apache’s htpasswd authentication, or restrict to a specific IP Range, or VPN connection etc
  2. Block only “admin” level functions: e.g specific authentication triggered for all /admin/ requests: again, trigger via .htpasswd or somesuch.
  3. Using Active Directory/LDAP to authenticate a user, and use groups specified outside the main application to load a user into a specific role
  4. Other SSO systems (shibboleth etc) integrated into IIS/Apache

Authentication within the application:

  1. Using traditional cflogin, and built in CF login functions
  2. Testing for session based variables specifically, i.e session.user.loggedinAt

Obviously there are a million other variations on this, but it does highlight that whatever your doing should at least consider these other methods.

What are you trying to protect?

Obviously, this is the first question you have to ask yourself. What is it that requires some level of authentication? Nine times out of ten, it’s the privilege of changing or viewing data, that much is pretty self explanatory. But it’s worth considering all data you’re storing, and how that might be used. For instance, blocking the web interface of an application is one thing, but what about potential API usage? RSS Feeds? Other connection types which don’t require a browser? For the Room Booking system (RBS after this point, I can’t be bothered with the typing), it’s relatively straightforward, at least at the moment. But in the future I’m planning to add RSS/iCS/JSON/XML feeds – perhaps only read only, but whatever authentication system, I’ve got to consider future usage.

What I ended up with:

In order for the RBS to be as flexible as possible to the most number of users, I ended up decided on (application specific) Role based permissions. In short, if person ‘bob’ is in role ‘admin’, and tries to do ‘x’ there needs to be some sort of permissions matrix indicating the admin role’s permission to do ‘x’. The important part to note here is that I’m not ever directly testing against a role. There should be  no conditional statements like if(isAdmin(user)) then do ‘x’; this leads to a nightmare later on when you suddenly decide you want to add a role, as for each ‘isAdmin()’ check, you then have to start adding ‘isEditor()’ or somesuch, and probably all over your application.

It’s better to build a privilege called ‘canDoX’ and test against that, as roles can more easily be added in the future.

Wheels lets us handle this quite neatly with two models, users & permissions. A user has a role, such as ‘admin’ or ‘editor’ etc; Then in the permissions table, you have a permission key, which is the name of your permission, i.e canDoX, and a column for each role, with a tinyint of 1 or 0 depending. So it’s a simple matrix of yes/no type data.

Then, onApplicationStart, we can check for an load in the matrix of permissions into the application scope for referencing later:

<cfset application.rbs.permissionsQuery=model("permission").findAll()>
 <cfloop query="application.rbs.permissionsQuery">
   <cfscript>
      application.rbs.permission["#id#"]={};
      application.rbs.permission["#id#"]["admin"]=application.rbs.permissionsQuery["admin"];
      application.rbs.permission["#id#"]["editor"]=application.rbs.permissionsQuery["editor"];
      application.rbs.permission["#id#"]["user"]=application.rbs.permissionsQuery["user"];
      application.rbs.permission["#id#"]["guest"]=application.rbs.permissionsQuery["guest"];
   </cfscript>
 </cfloop>

Once we’ve got these in the application scope, all we need are four functions to handle most of the permission based work:

<cffunction name="checkPermission" hint="Checks a permission against permissions loaded into application scope for the user" returntype="boolean">
	<cfargument name="permission" required="true" hint="The permission name to check against">
	<cfscript>
		if(_permissionsSetup() AND structKeyExists(application.rbs.permission, arguments.permission)){
			return application.rbs.permission[arguments.permission][_returnUserRole()];
		}
	</cfscript>
</cffunction>

<cffunction name="checkPermissionAndRedirect" hint="Checks a permission and redirects away to access denied, useful for use in filters etc">
	<cfargument name="permission" required="true" hint="The permission name to check against">
	<cfscript>
		if(!checkPermission(arguments.permission)){
			redirectTo(route="denied", error="Sorry, you have insufficient permission to access this. If you believe this to be an error, please contact an administrator.");
		}
	</cfscript>
</cffunction>

<cffunction name="_permissionsSetup" hint="Checks for the relevant permissions structs in application scope">
	<cfscript>
		if(structKeyExists(application, "rbs") AND structKeyExists(application.rbs, "permission")){
				return true;
		}
		else
		{
			return false;
		}
	</cfscript>
</cffunction>

<cffunction name="_returnUserRole" hint="Looks for user role in session, returns guest otherwise">
	<cfscript>
		if(_permissionsSetup() AND isLoggedIn() AND structKeyExists(session.currentuser, "role")){
			return session.currentuser.role;
		} else {
			return "guest";
		}
	</cfscript>
</cffunction>

So all you’d need to do using the above is:

<cfscript>
if(checkPermission("canDoX")){
// Do stuff
}
</cfscript>

The checkPermissionAndRedirect() function is very similar, and can be used on a filter level in your controller to just push away the user if they shouldn’t be accessing a view/function etc

i.e, on my Locations.cfc controller, where CRUD updating for the RBS board locations takes place:

filters(through="checkPermissionAndRedirect", permission="accessLocations");

The advantages of this approach are:

  • You’ve got an ‘out-the-box’ system which doesn’t require extensive server setup
  • You could extend this principle to allow for 3rd party authentication – i.e, if you’re going via LDAP, once a user is approved, a ‘local’ account could easily be created which duplicates core information such as email/name from the initial authentication (see an example I did a few years ago here)
  • Permissions are cached in the application scope, meaning you potentially avoid another database hit for every user login
  • In the future, user based permissions which override role based permissions could easily be added: our checkPermission(“foo”) function could easily look for the equivalent key in the user’s session scope
  • Adding new permissions to use is as simple as adding a line in the permissions table
  • Each installation can redefine the abilities of each role to suit their needs: so in the RBS example, you could make every role do everything, and then use some other authentication method, or lock down guest users etc.

All this code can be seen in context at the GitHub Repo

v1.01 RoomBooking system released

So I admit that’s a pretty quick release cycle, but hey, I was inspired (or something). There’s some massive changes, most noticeably the addition of user authentication, roles, permissions, logging & password resets. All documentation has been moved to https://github.com/neokoenig/RoomBooking/wiki, where you can find a breakdown of all the settings, permissions, roles and upgrading instructions.

Things learned from creating the RoomBooking System

Since launching my little Room Booking system as an open source project, I thought it might be useful to highlight a few techniques used which others might find useful. Obviously, this isn’t to say that this is the ‘only way to do x’, but merely to flag some ways to approach common problems. All the below can be seen in context at the gitHub repo

events/functions.cfm

One issue a had a while back was determining the IP address of a user. Now, historically, this wasn’t an issue, as cgi.remote_host worked fine. However, since Railo’s installer changed the way it forwards requests from Apache (mod_jk-> mod_proxy), it always saw the remote IP as 127.0.0.1. This function checks the incoming request header and tries to grab the IP there, rather than in the CGI scope.

<cffunction name="getIPAddress" hint="Gets IP address even from Railo" returntype="String">
<cfscript>
   var result="127.0.0.1";
   var myHeaders = GetHttpRequestData();
    if(structKeyExists(myHeaders, "headers") AND structKeyExists(myHeaders.headers, "x-forwarded-for")){
      result=myHeaders.headers["x-forwarded-for"];
    }
    return result;
</cfscript>
</cffunction>

events/onrequeststart.cfm

This simply checks every incoming request for the URL or FORM scope, loops over each key in the struct, and trims the whitespace.

<cfif StructCount(form)>
	<cfloop collection="#form#" item="key">
		<cfset form[key] = Trim(form[key])>
	</cfloop>
</cfif>
<cfif StructCount(url)>
	<cfloop collection="#url#" item="key">
		<cfset url[key] = Trim(url[key])>
	</cfloop>
</cfif>

Personally, I think cfWheels should do this by default, but then, it would probably break a load of people’s applications. Simply put, I can’t imagine a case where I’d want whitespace at the beginning or end of any key in my url or form scope.

view/helpers.cfm

This one was an interesting one – Javascript often passes it’s values in Epoch time, i.e number of seconds since 1970; I needed to convert this to localTime in a few places. Surprisingly, I’ve never had to do this before!

<cffunction name="eToLocal" hint="convert epoc to localtime">
	<cfargument name="e">
	<cfreturn DateAdd("s", arguments.e ,DateConvert("utc2Local", "January 1 1970 00:00"))>
</cffunction>

So Bootstrap3 has panels. I love panels. But I’m lazy, I don’t want to type out the markup for a panel all the time. So these two helpers do it for me:

<cffunction name="panel" hint="Renders a bootstrap Panel">
    <cfargument name="title" required="true">
    <cfargument name="class" default="panel-default">
    <cfargument name="ignorebody" default="false">
    <cfset var r ="">
    <cfsavecontent variable="r"><Cfoutput>
    <!--- Start Panel --->
    <div class="panel #arguments.class#">
        <div class="panel-heading">
            <h3 class="panel-title">#arguments.title#</h3>
        </div>
        <cfif !arguments.ignorebody>
        <div class="panel-body">
        </cfif>
    </Cfoutput>
    </cfsavecontent>
    <cfreturn r />
</cffunction>

<cffunction name="panelend" hint="Close Panel">
    <cfargument name="ignorebody" default="false">
    <cfif !arguments.ignorebody>
        <cfreturn "</div></div>" />
    <cfelse>
        <cfreturn "</div>" />
    </cfif>
</cffunction>

Usage:

#panel(title="foo")#
.. things here
#panelEnd()#

Sometimes Bootstrap needs it’s content to be flush with the panel, so you occasionally need to ignore the panel-body – flush list-group-items – I’m looking at YOU.

#panel(title="foo", ignorebody=true)#
<ul class="list-group">
<li class="list-group-item">FOO</li>
</ul>
#panelEnd(ignorebody=true)#

Previously, I’d have done this using custom tags, i.e , but with the added import needed on every page, I find this a *lot* quicker/easier.

views/layout.cfm

One of things I like to do is have all the CSS in the header, and all the JS in the footer. So what happens when you want to do your JS stuff in your view folders? Here’s one solution..

In your view file, simply wrap your JS bits in cfsavecontent, i.e

<cfsavecontent variable="request.js.mystuff">
<script>// JS goes here</script>
</cfsavecontent>

Then in your layout file, after all your calls to jQuery etc:

<cfif structkeyexists(request, "js")>
<cfloop list="#structKeyList(request.js)#" index="key"><cfoutput>#request.js[key]#</cfoutput></cfloop>
</cfif>

The only thing this doesn’t really deal with is calling JS in a specific order, as structs are by their nature, unordered. I guess you could turn this principle into an array based system easily enough though: generally speaking, I haven’t found needing to write JS over many view files which needs to be executed in a specific order. YMMV.

bookings/index.cfm

This bit of JS is very useful:

 $('body').on('hidden.bs.modal', '.modal', function () {
        $(this).removeData('bs.modal');
});

Bootstrap caches modals which have had dynamic data inserted in, so initially, when I was building it, every time I clicked on an event, the modal would display the first event I’d click on’s data. This destroys the modal data after you close it. Handy.

And another thing…

The last thing to note is that this has an example of using Ajax in cfWheels, examples of which are fairly rare. If you’re interested in how that’s done, look at the javascript call in bookings/index.cfm under event sources: it’s just a jQuery ajax request. Now look at controllers/Eventdata.cfc = you’ll see the renderWith call, and if you look at the function ‘prepdata’, you’ll see me converting a query into a array of data (in the format I wanted it), which is then passed back by wheels using renderWith(events);

So there you go. If, of course, you guys have better ways of handling the above, I’d love to hear them.

New Open Source Project – Room Booking System

I’m pleased to announce a new open source project for a room booking system – it’s basically a backend to the excellent fullcalendar.js jQuery plugin, using cfWheels as the back end.

Features include:

  • All (well most) configuration done via database settings
  • Uses the DBMigrate plugin for cfWheels for easy future updates
  • Uses the FullCalendar.js month/day/week views
  • Allows for multiple locations, which can be filtered from the main view
  • Each location can be colour coded, and have room layout options
  • Allows you to bulk create events, so easy to add placeholders for repeating series
  • Completely configurable via web interface, all you need to do is setup a datasource called ‘roombooking’
  • Has a theme switcher so you can use/try any theme on http://bootswatch.com/
  • Can send confirmation emails on a new booking

You can download the code from gitHub – https://github.com/neokoenig/RoomBooking or try a demo at http://roombooking.oxalto.co.uk. Some screenshots below. Full documentation at http://roombooking.readme.io/

 

Feel free to contribute or log a bug at https://github.com/neokoenig/RoomBooking – hopefully it’ll be useful for someone!

cfWheels Relationships (Part 5)

HasManycheckbox() is obviously only one way of updating these relationships. The other main way is via cunning uses of includePartial().

Let’s return to our contact’s email addresses – remember the contact ‘hasMany’ of those. We need a way to a) list their existing email addresses, b) add new ones (preferably within the same update form as the main contact details and c) edit or delete existing addresses.

When you need to display/update more information that what a checkbox could hold, includePartial() comes to the rescue. Whilst I’m demonstrating this with the email address field, which just has a single value, there’s nothing stopping you adding additional fields for this example.

In our Contact’s edit form, we can simply do this:

<fieldset>
<legend>Email</legend>
    #includePartial(contact.emailaddresses)#
    #includePartial("emailaddressNew")#
</fieldset>

And then create a partial called _emailaddress.cfm in the /views/contacts/ folder, containing:

<cfoutput>
#btextField(
    objectName="contact",
    association="emailaddresses",
    position=arguments.current,
    property="email",
    label="Email Address",
    class="span4"
)#
</cfoutput>

This should look pretty familiar – after all, it’s *almost* the same as a bog standard textField on a model, except we’re listing the association, and crucially, the postition.

So if we’ve got multiple email addresses, what does the generated HTML look like?

<label class="control-label" for="contact-emailaddresses-2-email">Email Address</label>
<input class="span4" id="contact-emailaddresses-2-email" maxlength="500" name="contact[emailaddresses][2][email]" value="joe3@bloggers.com" type="text">
<label class="control-label" for="contact-emailaddresses-3-email">Email Address</label>
<input class="span4" id="contact-emailaddresses-3-email" maxlength="500" name="contact[emailaddresses][3][email]" value="joe2@bloggers.com" type="text">

The [emailaddresses][3][email] part of the naming convention translates to “object[association][id][property]”.

And when submitted, with params.contact, we can see a nested struct:

If we’ve included the association when we do the contact updating, i.e (contact.update(params.contact);) these properties will automatically update, with no extra code.

That’s all good for existing entries, but what about adding new ones? Wouldn’t it be nice if we could just “reinclude” that partial to add a new form? If you try that, you’ll hit the ‘arguments.position’ error – as that partial is expected a numeric value for the position in the array that the property is in. Even if you try and set a tickCount as a unique identifier, you’ll hit snags too. This is probably the most annoying thing (currently, i.e wheels 1.1.8) – if someone’s got an elegant solution to this, please let me know 🙂 At the moment I fall back to either adding via a separate params struct, and testing for it’s existence, or doing it via Javascript.

Right, that’s all for now – thoughts on improving this greatly recieved.

cfWheels Relationships (part 4)

So far we’ve setup the database tables and the models, so now we need to start thinking about getting and updating this contacts data, including the associated nested properties.

Outputting a simple add/edit form should hopefully be familiar to you by now (if not I recommend checking out the cfwheels screencasts: http://cfwheels.org/screencasts ). When updating our nested properties, there are several methods, each with pros & cons.

Firstly, let’s have a look at HasManyCheckBox();

The hasManyCheckbox() helper is really, really useful. I’m going to use our technologies relationship, as this is just the sort of data which is designed to be displayed/updated in this fashion.

#hasManyCheckBox(
    objectName="Contact",
    association="contactTechnologies",
        keys="#contact.key()#,#id#",
        label=name)#

Now what does this actually do? The loop itself should be obvious – we’re looping over *all* the potential values for technologies – after all, we want to be able to list those entries which aren’t ticked too: but we need to be able to make sure that the entries which are present in the join table are properly ticked. ObjectName=”Contact” is our parent object – the one which holds the nested properties. Association is the name of the nested property itself. Next, you have the most important part, the composite key. Remember when I mentioned about the column key order in the database construction for our join table? *This* is why it’s important: the order of the keys here *MUST* reflect the order in the database schema. I’ve used contact.key() which allows me to do this on a new model (which won’t actually have a primary key per se – yet).

So what does this wonderful piece of code actually output?

<label class="checkbox" for="Contact-contactTechnologies-1-1-_delete">ColdFusion<input id="Contact-contactTechnologies-1-1-_delete" type="checkbox" name="Contact[contactTechnologies][1,1][_delete]" value="0" checked="checked" /><input id="Contact-contactTechnologies-1-1-_delete-checkbox" type="hidden" name="Contact[contactTechnologies][1,1][_delete]($checkbox)" value="1" /></label>

<label class="checkbox" for="Contact-contactTechnologies-1-4-_delete">CSS<input id="Contact-contactTechnologies-1-4-_delete" type="checkbox" name="Contact[contactTechnologies][1,4][_delete]" value="0" /><input id="Contact-contactTechnologies-1-4-_delete-checkbox" type="hidden" name="Contact[contactTechnologies][1,4][_delete]($checkbox)" value="1" /></label>

<label class="checkbox" for="Contact-contactTechnologies-1-5-_delete">HTML5<input id="Contact-contactTechnologies-1-5-_delete" type="checkbox" name="Contact[contactTechnologies][1,5][_delete]" value="0" /><input id="Contact-contactTechnologies-1-5-_delete-checkbox" type="hidden" name="Contact[contactTechnologies][1,5][_delete]($checkbox)" value="1" /></label>

<label class="checkbox" for="Contact-contactTechnologies-1-6-_delete">jQuery<input id="Contact-contactTechnologies-1-6-_delete" type="checkbox" name="Contact[contactTechnologies][1,6][_delete]" value="0" /><input id="Contact-contactTechnologies-1-6-_delete-checkbox" type="hidden" name="Contact[contactTechnologies][1,6][_delete]($checkbox)" value="1" /></label>

etc. As you should be able to see from the above, there’s actually some fairly complex stuff going on: imagine it as cfWheels by default adding all the associations & relationships, then deleting all the relationships, then by checking a checkbox, you save that association from being deleted. Looking at the dumped params struct makes this more obvious:

The ‘1,1’, ‘1,2’ struct keys are the composite keys: 1 representing the contactid, and the second representing the technology (in this case). If the checkbox is checked, then the _DELETE sub struct returns false.

What’s nice about this approach is that you don’t have to do anything extra; when submitting and creating the final contact object, you just have to make sure you include the associations.

‘Why that’s awesome!’ I hear you say. Yes, yes it is. BUT. It doesn’t help us in a few areas.

1) We can’t add new values to the list easily – i.e I can’t add a new technology
2) We can’t edit the associated properties easily – i.e I can’t edit an existing technology

Of those two points, I would argue editing existing properties from within the contact update/add form is perhaps not the place for it – these properties could apply to other contacts, so pushing them out into their own form is probably better from a user perspective – there’s then no chance of the user thinking ‘oh I’ll just change ColdFusion to Railo’ and then the horror when all other contacts then list Railo too.

Adding new values to the technology list is however something I see happening. To do this, I’ve found the easiest solution is to just create a new form field tag, and then test for the values when updating the contact object.

This means when we submit our update or create request, we need to test for the existence of the field, return the created key, and then add the association manually.

I’ve found the easiest way to do this (so far) is to add it to a new private function: So our update function calls our new checking function ‘checkForNewTechnologies()’:

contact.update(params.contact);
	if(contact.hasErrors()){
         renderPage(action="edit");
	}
        else {
	checkForNewTechnologies();
	 flashInsert(success="The contact was updated successfully.");
	 redirectTo(action="view", key=contact.id);
}

Which looks like:

<cffunction name="checkForNewTechnologies" access="private">
<cfscript>
  if(structkeyexists(params, "newtechnology") AND len(params.newtechnology) GT 2) {
		nTechnology=model("technology").create(
			name=params.newtechnology
		  );
		nTechnologyAssociation=model("contacttechnology").create(
			contactid=contact.key(), technologyid=nTechnology.key()
		 );
  }
</cfscript>
</cffunction>

Obviously, this is just one way of many to do it. You could add the new technology via Ajax, return the generated ID, then replicate the form HTML via JS to pass the new value in along with the other checkboxes: this is quite a cumbersome technique as you rely on recreating the wheels generated HTML manually, but might have it’s uses.

cfWheels Relationships (part 2)

Now it’s just a matter of representing these relationships in cfWheels so we can create/update/delete some data.

models/Contact.cfc

<cfcomponent extends="Model" output="false">
    <cffunction name="init">
		<cfscript>
                // Model init functions
		property(name="firstname", label="First Name");
		property(name="lastname", label="Last Name");

		//Calculated Properties
		property(name="fullname", sql="CONCAT(firstname, ' ', lastname)");

		// Relationships
		hasMany(name="emailaddresses", dependent="deleteAll");
		hasMany(name="contacttechnologies", dependent="deleteAll");
		hasMany(name="contactcompanies", dependent="deleteAll");

		// Nested properties
		nestedProperties(associations="
                      emailaddresses,
                      contacttechnologies,
                      contactcompanies", allowDelete=true);

        </cfscript>
    </cffunction>
</cfcomponent>

So I’ve done a couple of things here worthy of note: I’ve set some default values for the firstname and lastname properties – namely the label name: I want this to display as ‘First Name’ rather than the default ‘firstname’ which is what the label=”” attribute is for. I’ve also added a very simple calculated property, of fullname, which takes the first and last names and puts them together at a database level. You can imagine how this can become very useful indeed – if I had prefix, suffix, middlename columns, I could build up various additional properties using those too.

Lastly, you’ll see the relationships & nested properties; emailaddresses has the ‘dependent=”deleteAll” attribute, which means when I delete a contact, the dependent email addresses will also be deleted. As the other relationships are also dependent to that contact, they’ll be deleted too – but bear in mind, this won’t delete the ‘source’ data, i.e the technologies or companies tables which is crucial as these are being used by other entities in the application. The nested properties call allows us to literally ‘nest’ models *within* the contacts model. More on this later.

models/Emailaddress.cfc

<cfcomponent extends="Model" output="false">
    <cffunction name="init">
		<cfscript>
        // Model init functions
		 belongsTo("contact");
        </cfscript>
    </cffunction>
</cfcomponent>

As this is our simplest relationship, an Email address is very easily represented with a simple ‘belongsTo’ call.

models/Technology.cfc

<cfcomponent extends="Model" output="false">
    <cffunction name="init">
		<cfscript>
		// Relationships
		hasMany(name="contacttechnologies");
        </cfscript>
    </cffunction>
</cfcomponent>

As this is ‘on the other end’ of the join for contact-technologies, we need to refer the technology model back to the join model.

models/Technology.cfc

<cfcomponent extends="Model" output="false">
    <cffunction name="init">
		<cfscript>
       	// Relationships
		belongsTo(name="contact");
		belongsTo(name="technology");
        </cfscript>
    </cffunction>
</cfcomponent>

And here is the join model – it ‘belongsTo’ both contact.cfc and technology.cfc and allows us to join the two.

models/Company.cfc

<cfcomponent extends="Model" output="false">
    <cffunction name="init">
		<cfscript>
		// Relationships
		hasMany(name="contactcompanies");
        </cfscript>
    </cffunction>
</cfcomponent>

models/Contactcompany.cfc

<cfcomponent extends="Model" output="false">
    <cffunction name="init">
		<cfscript>
       	// Relationships
		belongsTo(name="contact");
		belongsTo(name="company");
        </cfscript>
    </cffunction>
</cfcomponent>

Lastly, our company and contactcompany models.

Visualising the Models

I tend to find being able to visualise the data structure more useful than relying solely on code. What do these models look like if we just create a new one?

Let’s take the Contacts model, and simply call:

contact=model("contact").new();

The result?

As expected, but… what about our email addresses?  How do we get them into our nice new contact call?

We need to create a new instance of the emailaddress model, and pass it to the .new() call as a named argument. As there may be more than one email address, we need to put our email address model within an array, so cfWheels can keep track of multiple nested properties.

If we try:

newEmailaddress[1]=model("emailaddress").new();
contact=model("contact").new(emailaddresses=newEmailaddress);

We get:

So we now have an (empty) nested email address model within the new contact. Handy.

We can also extend this concept for the other relationships:

newEmailaddress[1]=model("emailaddress").new();
newContactCompany[1]=model("contactcompany").new();
newContactTechnology[1]=model("contacttechnology").new();

contact=model("contact").new(
	emailaddresses=newEmailaddress,
	contactCompanies=newContactCompany,
	contactTechnologies=newContactTechnology
);

Well, this is all very well, but what do these models look like with some data in them?

cfWheels Relationships (part 3)

Let’s look at an example call to a contact, but without any of it’s relationships.

contact=model("contact").findOne(where="id=1");

So, the simplest possible call – find me one contact row where id = 1.

Well, it’s good to see our createdAt field got autofilled, and our calculated property of fullname made it through too. deletedAt is null, which is also good. The above call would return false if deletedAt contained a value.

Let’s do the same call, but get those email addresses:

contact=model("contact").findOne(where="id=1", include="emailaddresses");

That’s better! Incidentally, if there were no email addresses, we’d get an empty array like so:

Let’s extend this to the other relationships:

contact=model("contact").findOne(where="id=1",
		include="emailaddresses,contactCompanies,contactTechnologies");

So here we can see the join table relationship nested properties too. Now, here’s the catch. How do we get the data from the otherside of the join?

We can’t do this, as we’re returning a single entity with objects:

contact=model("contact").findOne(where="id=1",
include="emailaddresses,contactCompanies(company),contactTechnologies(technology);

We *can* do it if we return this record as a query (which is the default for a ‘findAll’ call):

contact=model("contact").findAll(where="id=1",
include="emailaddresses,contactCompanies(company),contactTechnologies(technology),
returnAs="query");

However, whether you actually want to do this will be dependent on your application. Sometimes, this will make perfect sense, especially if you’re calling data in any sort of tabular fashion. But if you’re calling your model in order to populate a form (for example) then this complicates matters. More on ‘hasManyCheckBox’ and associated solutions later.

cfWheels Relationships (part 1)

No, this isn’t going to be a guide about getting on with your loved one (unless of course, your loved one is a certain open source coldFusion framework) but will specifically look at all the different types of relationships in cfWheels, and what they mean in terms of real world application design. I’m going to assume you have a working knowledge of cfWheels (objects/models/controllers/views/routes etc) as the documentation over at cfwheels.org is very good, and well worth digging into.

For this series, I’m going to take the old default example application of a ‘people’ database (or in this context, a contacts database).

Normalising Data

The old addage ‘DRY’ (or Don’t Repeat Yourself) applies just as much to your data, as it does to your controller & view code. I don’t consider myself a DBA by any stretch of the imagination, but it simply makes sense – don’t duplicate data (except in extreme circumstances – there I said it). With that in mind, let’s have a think about what this application should do, and how this might reflect our model conventions and model relationships.

The Contact model:

Well, at it’s core, a contacts directory needs, yes, you guessed it, a list of people. Let’s call them ‘Contacts’. At a mimimum, we’re going to need a contact.cfc model. This should contain any information which is unique to a contact, and has one value.

The table ‘contacts’ might look like this:

contacts
—————-
id [key, int, autoincrement]
firstname [varchar, 255]
lastname [varchar, 255]
createdAt [datetime]
updatedAt [datetime]
deletedAt [datetime]

Pretty simple – an id to reference with, firstname & lastname fields which we can add a calculated property to later to get a ‘fullname’ value, and three cfWheels specific fields, which will auto update – when it’s created, updated or deleted. Naturally, our id field will need to autoincrement, and be an integer.

So what about that contact’s data? Let’s assume we want to store the following data about this contact:

  • One or more email addresses, which are specific to that contact. This is a ‘one to many‘ relationship – one contact, multiple email addresses. It could be said a contact ‘hasMany‘ email addresses, and that those email addresses ‘belongTo‘ that contact.
  • A set of related technologies such as ‘mysql’, ‘php’, ‘coldFusion’, ‘cfWheels’. A contact might have many of these, and likewise, ‘mysql’ might need to refer to multiple contacts. So whilst a contact ‘hasMany’ technologies, they’re not exclusive to that contact.
  • A company relationship, such as ‘Adobe’, ‘Microsoft’, ‘Apple’ – we’d want to reuse these companies for other contacts too. Companies might also contain additional data such a postal address. We’d also want to store data about that relationship to that company – i.e a contacts postition, and be able to easily retrieve data about that company when looking at a contact.

Let’s take all these in turn, as each presents different challenges when referring back to our contact.

One or more email addresses: So a new table is called for to store our email addresses.

emailaddresses
——————-
id [key, int, autoincrement]
email [varchar, 255]
createdAt [datetime]
contactid [int]

As before, an id to reference with, an email column to store the actual value, and a createdAt so we know when it was added. To me, this data is less important to keep track of in terms of audit, so I’m ignoring the updatedAt/deletedAt fields. The contactid field here is the (excuse the pun) ‘key’ to the whole excercise. This ties in the email address to the contact, after we setup our model relationships later. This is probably the simplest relation type in this example.

A set of related technologies: We’ll need a table to store our ‘technologies’ – this will be a very simple table.

technologies
—————
id [key, int, autoincrement]
name [varchar, 255]

That’s all we need to store this, it’s essentially a glorified list of categories. But how do we store which contact has a technology, and which technologies refer to what contacts? We need a join table, which will simply be the referring id’s of the contact and the technology.

contacttechnologies
—————
contactid [key, int]
technologyid [key, int]

Note how I’ve named the table – and also the order of the columns. As “contact” has come first in the column name, I’ve made sure the column order reflects this – the reason why becomes obvious later on. As both columns are keys, this means we can’t have any duplicate rows – that’s a good thing – we don’t want random duplicate relationships popping up anywhere.

So this is what it looks like so far:

Company relationships

Our company relationships mean we need two things – one, a place to store our companies, and two, a join table to store the relationship in the same fashion as contacts – technologies as before.

companies (this is the same table structure as contacts for this example)
—————-
id [key, int, autoincrement]
name [varchar, 255]
createdAt [datetime]
updatedAt [datetime]
deletedAt [datetime]

contactcompanies
—————
contactid [key, int]
companyid [key, int]

This pattern should start to look a little familiar – it’s basically the same as the email address – contact relationship.

What does our db structure look like now?

Next, we’re going to look at representing these relationships within cfWheels.