A Recursive Function to Gather CFC Metadata for Inherited Properties
Posted At : December 21, 2009 1:40 PM | Posted By : Bob Silverberg
Related Categories: ColdFusion, CF ORM Integration, BPO
Charlie Stell made a comment on my last post about my Base Persistent Object project, pointing out that my populate method did not take inherited properties into account. When I first developed it I wasn't using inheritance with ColdFusion ORM, as I'm coming from a Transfer background and Transfer doesn't really support inheritance. On a recent project using CF ORM I did implement an inheritance scheme and decided to update my populate method to support that.
The reason that the current populate method does not support inheritance is that is simply looks at the properties array that is included in the cfc metadata of the current object. That array of properties does not include any inherited properties. To find those you need to look at the extends key in the metadata structure and check for any properties of the object that the current object extends, and, of course any objects that that extends, etc. You could potentially have several levels of parents as you move up the inheritance hierarchy.
I decided that the best approach would be to write a recursive function that would return all of the properties of an object as an array, including all properties of any of the object's parents. In his comment Charlie provided some sample code that he is using, and I took his suggestion and made a couple of changes to it. Here's the code:
2 local.prop = 1;
3 if (structKeyExists(arguments.md,"properties")) {
4 for (local.prop=1; local.prop <= ArrayLen(arguments.md.properties); local.prop++) {
5 if (not ArrayContains(arguments.props,arguments.md.properties[local.prop].name)) {
6 arrayAppend(arguments.props,arguments.md.properties[local.prop]);
7 }
8 }
9 }
10 if (arguments.md.extends.fullname neq "WEB-INF.cftags.component") {
11 arguments.props = collectAllProperties(arguments.md.extends,arguments.props);
12 }
13 return arguments.props;
14}
To use this function, you simply pass in the metadata of an object into the md argument, so if I wanted to get the metadata for all properties for a User object I would do something like:
2userProperties = collectAllProperties(getMetadata(User));
The collectAllProperties function then starts by adding all of the User object's properties to an array, after which it checks to see which cfc the User object extends. Let's say in this example that it extends a Person.cfc. Because Person.cfc isn't WEB-INF.cftags.component (which all cfcs ultimately extend), it then calls the collectAllProperties() function recursively, passing in the extends key, which is in itself a complete package of cfc metadata, and it also passes it the current array of properties that is being built. This process will continue, with each cfc in the inheritance hierarchy being interrogated, adding properties to the array as long as they don't already exist in the array. When the recursion finally comes to a parent that is WEB-INF.cftags.component it knows it doesn't have to go any further, so the recursive calls stop and the full array of properties is returned to the caller.
I haven't added this logic to my Base Persistent Object project yet, as there are a few other changes I want to implement as well, but I thought I'd put this simple function out there for anyone that's interested.
BTW I didn't test that... :)
1. That's only going to return the property names, but I want the complete metadata for the properties, not just their names.
2. I don't just want the properties, as defined by Hibernate, I also want the relationships, which (I think) are not returned by getPropertyNames().
3. I don't want the list of properties limited to those defined as persistent. I may have additional properties on my object, which I'm going to use during processing but not persist to the database, and Hibernate doesn't know anything about those.
The text is in German but the code should be understandable
http://www.danielschmid.name/post.cfm/instanz-date...
I didn't think to include non-persisted fields, but I can see where this would pretty helpful.
http://henrylearnstorock.blogspot.com/2010/01/cf9-...
https://www.hibernate.org/hib_docs/v3/api/org/hibe...
I should take some time to come up with a solution using pure Hibernate metadata too, though.