The ultimate find by method for ORM: Part II

Word Count: 354

In part I I took a look at a basic implementation of a dynamic find by method. Tony Nelson was quick to point out that this would not work if you had entity names that contained AND such as Candidate. I went back to the drawing bored and with a nice idea from Tony I cam up with another solution. His example was a little more dynamic ubt please understand this is a stand alone example for a reason. This functionality is going to be created in my abstract service layer. Anything that is going to be common among all service objects should not be repeated.

So how are we going to fix this problem with the AND keyword. The AND join is going to tell us that we need to find rows by 2 different fields. This could be improved upon to handle many ands but I really don't see myself ever needing more than 2 lookups for a find by method. If we know what entity we are working with we can do a lookup for the properties of that entity. In the hard coded example case we know we are working with the Art entity so we can do the following. Given the fact that we know the valid properties of the entity we can do the following.

  1. Does any part of the missing method name after findby contain AND
  2. If no - then just return that as the lone field
  3. If yes - are the left and right sides of and valid properties? If so we have a join. If not we have a field name.

Remember this whole time we have been working with the dynamic find by method. The find by method is looking for exact methods. If we wanted to we could use this same logic for a search by method. These and many more methods are going to provide a constant API for talking with Entities. I will be posting my abstract service layer shortly so stay tuned. Until then here is the stand alone service component.

Comments

#1 Posted By: Tony Nelson Posted On: 12/21/09 11:28 AM
Good stuff. I wonder if there's an easier way to get the entity name than hardcode/rely on file name. Maybe add an /** @entityName art */ to the component metadata?

Not to nitpick, but you'll want to change listContainsNoCase() to listFindNoCase() inside isValidProperty(), as there's quite a difference between the two. :)
#2 Posted By: Ben Nadel Posted On: 12/21/09 12:26 PM
I like the idea of running the property names against the property collection - slick.

I wonder if it would be too ugly to delimit the "by" properties with a $ sign. Like:

CFC::findBy$ArtNameAnd$ArtPrice( "foo", 100 )

It might just help make the parting easier.
#3 Posted By: Ben Nadel Posted On: 12/21/09 12:27 PM
Hmm, after reading that again, it does look kind of junky :) Disregard.
#4 Posted By: Dan Vega Posted On: 12/21/09 12:30 PM |
Author Comment
@Ben - I found a solution to the AND problem. The code above will allow you to do the following.

findByArtNameAndArtPrice("foo",100)
#5 Posted By: Ben Nadel Posted On: 12/21/09 12:32 PM
@Dan,

Sorry, I didn't mean to imply your code didn't work. I was just thinking out loud.
#6 Posted By: Dan Vega Posted On: 12/21/09 12:33 PM |
Author Comment
@Tony - Thanks for catching that. Remember this is just a concrete example. What I am doing now is create a service that extends my Abstract Service. So

UserService extends="AbstractService" {

property entity;

public UserService function init(String entityName){
this.setEntity(arguments.entity)
}

}

If you do it that way your abstract entity can always refer to getEntity()
#7 Posted By: Dan Vega Posted On: 12/21/09 12:34 PM |
Author Comment
Ok Ben I just thought I didn't make that clear :) I like the idea of saying exactly what I want therefor the field1AndField2 just feels right. Though as I stated this is not very dynamic as it only expects 1 AND so I may need to fix that.
#8 Posted By: Dan Vega Posted On: 12/21/09 12:41 PM |
Author Comment
@Tony - On second thought I really like your idea. Adding it as meta data makes it easier. Then when you create your concrete class "UserService" all it needs to do is extend and provide an entity name. Good thinking!
#9 Posted By: Dan Vega Posted On: 12/21/09 12:50 PM |
Author Comment
This does the trick

public string function getEntity(){
   var meta = getMetadata(this);
   var entity = "";

   if(structKeyExists(meta,"entity")){
      entity = meta.entity;
   }

   return entity;
}

Now you can create a concrete class like so

/**
* @displayname Product Service
* @accessors true
* @entity Product
*/
component extends="AbstractService" {

   public ProductService function init(){
      return this;
   }
}

Anyone else have fun with this or I am the only dork in the room?
#10 Posted By: Elias Perez Posted On: 4/2/10 8:13 PM
I found something interesting that I think I should share. I created a method that cleans up a struct only with valid properties of the Entity so I can run the List() method more effectively on the AbstracService. Below is the method created.

/**
* Removes any empty structure keys from within a structure.
*
* @param structure Structure to modify. (Required)
* @return Returns a structure.
*/
function removeEmptyStructureKeys(structure) {
    var newStructure = structNew();
    var keys = structKeyList(arguments.structure);
    var name = "";
    var i = 1;
      
    for (;i lte listLen(keys);i=i+1) {
    name = listGetAt(keys,i);
         writeDump( getMetadata(this));
         writeDump(isValidProperty(name));abort;
         if (not IsNull(arguments.structure[name]) && isValidProperty(name) ) {
    newStructure[name] = arguments.structure[name];
    }
    else{
    StructDelete(arguments.structure, name, "True");
    }
    }
    return newStructure;
   }

/*List Function*/
public any function list(Struct filter=structNew(),String sortorder="",Struct options=structNew(),Boolean asquery=true){
   
      
      var rows = EntityLoad(getEntity(),removeEmptyStructureKeys(arguments.filter),arguments.sortorder,arguments.options);
      
      //Return as query
      if(arguments.asquery){
         return entityToQuery(rows);
      }
      return rows;
   }


As you can see I use the isValidProperty(name) method to check against the entity properties. Well when I ran the list function passing a structure I found that the removeEmptyStructureKeys was removing the ID property of the entity which is also the Primary Key (fieldtype="id") on the table so if I want to load by ID using the list() it will give me an error. When I dump the getProperties() method I found that the ID was not listed as a property Interesting! Do you know why this is happening, Ben, Dan, Tony?


Post Your Comment

Leave this field empty







Show Captcha

If you subscribe, any new posts to this thread will be sent to your email address.

Copyright © 2007 Dan Vega | BlogCFC was created by Raymond Camden. This blog is running version 5.8.001.