Photo from Chile

A Recursive Function to Gather CFC Metadata for Inherited Properties

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:

view plain print about
1private array function collectAllProperties(required struct md,array props=ArrayNew(1)) {
2    local.prop = 1;
3    if (structKeyExists(,"properties")) {
4        for (local.prop=1; local.prop <= ArrayLen(; local.prop++) {
5            if (not ArrayContains(arguments.props,[local.prop].name)) {
6                arrayAppend(arguments.props,[local.prop]);
7            }
8        }
9    }
10    if ( neq "WEB-INF.cftags.component") {
11        arguments.props = collectAllProperties(,arguments.props);
12    }
13    return arguments.props;

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:

view plain print about
1User = EntityNew("User");
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.

Couldn't you get the properties by calling something like ormGetSessionFactory().getClassMetaData(listLast(getMetaData(this).fullname,".")).getPropertyNames()?

BTW I didn't test that... :)
# Posted By Tony Nelson | 12/21/09 2:20 PM
Certainly the getPropertyNames() function can be useful, but there are a few reasons why it won't give me what I need:

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.
# Posted By Bob Silverberg | 12/21/09 2:28 PM
Just an FYI - If you call getPropertyNames() you will get a list of all properties you defined which will include relationships. It will not tell you what type of relationships they are though or how they map. When you get the properties via meta data you can see everything you set in the relationship like fieldtype, cfc, fkcolumn,name,lazy etc...
# Posted By Dan Vega | 12/21/09 3:27 PM
I did something similar to mimik the getMemento() function often used.
The text is in German but the code should be understandable
# Posted By Daniel Schmid | 12/28/09 8:32 AM
Very cool!

I didn't think to include non-persisted fields, but I can see where this would pretty helpful.
# Posted By Charlie Stell | 1/4/10 6:29 PM
I did something similar as well :) (not recursive)
# Posted By Henry Ho | 1/22/10 9:10 PM
Relying on the metadata of the CFC can not always be exact as a user might start with the hbml configuration and not define the properties in the CFC's. Hibernate has ALL the metadata you can ever think about, you just need to look at the actual types of the properties. You can see if they are normal properties, entity relationships or collection relationships. You can then see ALL their type data.
# Posted By Luis Majano | 1/27/10 2:49 PM
@Luis, You are correct, using Hibernate metadata instead of cfc metadata would be "better", but I find using cfc metadata to be simpler, and as I'm just using this for my own purposes I know that I can control how much customization is being done to the hbmxml files.

I should take some time to come up with a solution using pure Hibernate metadata too, though.
# Posted By Bob Silverberg | 1/29/10 8:27 AM