So I am working on my abstract service layer and I decided to create a find by method. The idea for this came from a blog post by Joe Rinehart. At the time of writing this his blog was down so I decided to write my own. What this method will allow us to do is provide a generic find by API for our service. Before we get into how it works we should look at what it does. All of my examples are going to use the cfartgallery datasource so you can follow along at home.
In my first example I want to find all art records where the artists name is Michael. We could do something like this.
I just think being able to call any field dynamically is just intuitive and let's face it, FUN!
What if we wanted to find all of the paintings in the art table? All of the paintings have a media id of 1.
That is pretty easy as well but what if you wanted a query back.
We are not limited to finding by just one property. If we use the AND keyword we can chain together as many properties as we want.
So how does this all work? Well because the method findByMediaIdAndIsSoldAsQuery doesn't actually exist we are going to have to take advantage of the on missing method function. So here is the service component by itself. This is just a small piece of a much bigger abstract service that I am using that I will share with you soon.

#1 by Raul Riera on 12/19/09 - 6:09 PM
#2 by Tony Nelson on 12/19/09 - 6:13 PM
#3 by Dan Vega on 12/19/09 - 6:24 PM
@Tony - That is a really great point! Right now it would not work. I am wondering If I should change that to an ampersand - findByMediaId&IsSold
#4 by Tony Nelson on 12/20/09 - 8:13 AM
I was playing around with a way to get around this and here's what I came up with.
public any function onMissingMethod(string missingMethodName, struct missingMethodArguments) {
var i = "";
if (left(arguments.missingMethodName,6) eq "findBy") {
local.methodName = replaceNoCase(arguments.missingMethodName,"findBy","");
local.options = {asquery=false,unique=false};
if (right(local.methodName,7) == "AsQuery") {
local.options.asquery = true;
local.methodName = left(local.methodName,len(local.methodName)-6);
}
local.cache = structGet("variables.onMissingMethodFindByCacheSomewhatUniqueKey");
if (!structKeyExists(local.cache,arguments.missingMethodName)) {
local.entityName = listLast(getMetaData(this).fullName,".");
if (right(local.entityName,7) == "Service") {
local.entityName = left(local.entityName,len(local.entityName)-7);
}
local.properties = ormGetSessionFactory().getClassMetaData(local.entityName).getPropertyNames();
local.methods = [];
do {
for(i=1; i <= arrayLen(local.properties); i++){
local.property = local.properties[i];
if (left(local.methodName,len(local.property)) eq local.property) {
arrayAppend(local.methods,local.property);
local.methodName = replaceNoCase(local.methodName,local.property,"");
local.methodName = replaceNoCase(local.methodName,"and","");
}
}
} while (local.methodName != "");
local.cache[arguments.missingMethodName] = arrayToList(local.methods);
}
local.fields = local.cache[arguments.missingMethodName];
return findBy(local.fields,arguments.missingMethodArguments,local.options);
}
}
Hopefully that pasted ok...
Bear in mind that in order for this to work, the name of your service follows the pattern {Entity}Service, since it uses the service's metadata to abstract the entity name, which is then used to lookup the properties for the entity from the ORM session factory.
#5 by Dan Vega on 12/20/09 - 9:01 AM
#6 by Tony Nelson on 12/20/09 - 4:45 PM