CFWheels – Move Finders Into Your Model

One of the principles of CFWheels is not duplicating code. This can be achieved a number of ways, but one in particular can also save you some time. Typically our find() methods used to retrieve data have lots of conditions on them, setting the where clause or the order of the returned results, etc. One way to help clean up your controllers and speed up development is by moving those finders into your model to prevent duplication.

Consider the following controller. We are pulling a list of incomplete tasks ordered by their creation date. Now we could possibly have this same exact line of code scattered throughout our application. Anywhere that we would be pulling a list of incomplete tasks.

component name="Tasks" extends="Controller" output="false" {
	
	public void function index() {
		tasks = model('Task').findAllByComplete(value="false", order="createdAt DESC");
	}
	
}

By creating a custom method on our model object, we can clean up the controller code considerably and make it much more readable. Lets look at what the new controller code would look like.

component name="Tasks" extends="Controller" output="false" {
	
	public void function index() {
		tasks = model('Task').findIncomplete();
	}
	
}

Notice how we only need to call the single method now? Short, simple and concise.

Now lets look at how we achieve this by looking at our new Task model.

component extends="Model" output="false" {
	
	public any function findIncomplete() {
		return this.findAllByComplete(value="false", order="createdAt DESC");
	}
	
}

We simply move the same call we were using before into the new method and return the result. We now prefix the finder with this since we are in fact working with an instance of the Task object.

This really helps to reduce duplication of those parameterized finders in your controllers.

CFWheels – Find Through Association

When I first started learning CFWheels, I was so enamored with how simple it was to retrieve data from the database using the built-in ORM that I would code for days without taking full advantage of how much cleaner and simpler my code really could be. The more I worked with the ORM in Wheels, I learned more and more little tricks to help make my code much simpler. One of those tricks is to stop manually passing foreign keys into my find conditions and make the call through an association. Lets look at an example of how I used to do it.

Lets say we have a Project model that has many tasks and I want to retrieve all of the incomplete tasks for a given project.  Here are our two models:

Project.cfc

component name="Project" extends="Model" output="false" {

	public void function init() {
	  hasMany('Tasks');
	}

}

Task.cfc

component name="Task" extends="Model" output="false" {
	
	public void function init() {
	  belongsTo('Project');
	}

}

Now here is the controller with the old way of doing things:

component output="false" extends="Controller" {
	
	public void function show(){
		project = model('Project').findOneByKey(params.id);
		tasks = model('Task').findAll(where="projectid = #project.id# AND complete = false");
	}
	
}

Notice how I find the project I want and then pass the projectID in the task finder as the foreign key. Now this works, dont get me wrong. But there is a much cleaner way to do this.

component output="false" extends="Controller" {
	
	public void function show(){
		project = model('Project').findOneByKey(params.id);
		tasks = project.tasks(where="complete = false");
	}
	
}

By taking advantage of the relationships in our model, we can call the tasks through the project object and Wheels will automatically handle the foreign key relationship for us.

Isnt that much nicer?

Dynamic Finders in CFWheels

One of the unexpected treats to using CFWheels is how much more readable it can make your code. Im not a big fan of inline comments because I believe in self-documenting code. One of the features of CFWheels that helps to achieve this is “Dynamic Finders”. Here’s a quick example:

Consider this controller:

component extends="Controller" output="false" {

	public void function incompleteTasks(){
		tasks = model('Task').findAll(where="completed = false");
	}

	public void function lastIncompleteTask(){
		task = model('Task').findOne(where="completed = false", order="createdAt DESC");
	}
}

Notice the code is pretty simple, we are retrieving a query of the tasks that are completed by passing the where attribute into our findAll() method with the condition to check for. In the second method, we are retrieving the last incomplete task by passing in the same where clause and ordering the result by the createdAt date in descending order. This is already pretty clean and I love how simple Wheels makes this stuff. But, there is a more readable way to write this using dynamic finders.

component extends="Controller" output="false" {

	public void function incompleteTasks(){
		tasks - model('Task').findAllByComplete(false);
	}

	public void function lastIncompleteTask(){
		task = model('Task').findOneByComplete(value="false", order="createdAt DESC");
	}
}

Notice how we append the column attribute to the method that we want to filter by? In my opinion this is a better example of self-documenting code as its a bit more readable.

So what if we want to filter our results by more than one column? Wheels gives us that ability as well so we still dont have to go back to manually passing a where clause.

tasks = model('Task').findAllByCompleteAndUserid('false,1');

You simply add the other column after the “And” then pass your arguments as a list item to the method. Simple right?

Just a small often over-looked feature that could help make your code just a little cleaner.