onMissingMethod and Abstract Object - Part 2

I just did a bit more work on the onMissingMethod handler in my abstract bean cfc.

In case you are interested here are the differences between my implementation and John Whish's original which it was based on.

  • I have improved the code compared to the earlier post.
  • I have made the code work when you have multiple levels of CFC with one extending another. (John's original code only worked at the first level.
  • I have implemented the checking of what is gettable and settable.
  • I have removed the type checking that John put in, as I prefer to be able to load everything from a form into my bean and call validate to find out what is invalid. (You can always put John's checking back.)
Any way here is the code should you want it:

<cffunction name="onMissingMethod"
         hint="I act as a generic getter and setter for the class properties."
         access="public"
         description="Usage getXYZ() or setXYZ(value) where XYZ is the property name"
         returntype="any">

      <cfargument name="missingMethodName" type="string" />
      <cfargument name="missingMethodArguments" type="struct" />
      <cfset var propertyName = "" />
      
      <cfset getAccessors() />
      
      <cfswitch expression="#Left( arguments.missingMethodName, 3 )#">
         <cfcase value="get">
            <cfset propertyName = ReplaceNoCase( arguments.missingMethodName, "get", "" ) />
            <cfif StructKeyExists( variables.instance._properties, propertyName ) AND variables.instance._properties[propertyName].gettable>
               <cfreturn variables.instance[ propertyName ] />
            <cfelseif StructKeyExists( variables.instance._properties, propertyName )>
               <cfthrow message="Property '#propertyName#' is not marked gettable. Cannot get value." />
            <cfelse>
               <cfthrow message="Unknown property '#propertyName#'. Cannot get value." />
            </cfif>
          </cfcase>
         <cfcase value="set">
            <!--- I do not validate the type as I want to be able to save any string here and validate it later --->
            <cfset propertyName = ReplaceNoCase( arguments.missingMethodName, "set", "" ) />
            <cfif StructKeyExists( variables.instance._properties, propertyName ) AND variables.instance._properties[propertyName].settable>
               <cfset variables.instance[ propertyName ] = arguments.missingMethodArguments[1] />
            <cfelseif StructKeyExists( variables.instance._properties, propertyName )>
               <cfthrow message="Property '#propertyName#' is not marked settable. Cannot set value." />
            <cfelse>
               <cfthrow message="Unknown property '#propertyName#'. Cannot set value." />
            </cfif>
         </cfcase>
         <cfdefaultcase>
            <cfthrow message="Unknown method." />
         </cfdefaultcase>
      </cfswitch>
   </cffunction>
Here is the getAccessors method that is called to set up the structure. Now modified so its a structure of structures rather than three structures as in the earlier version.
<cffunction name="getAccessors"
         hint="I build a structure of public properties, data types and if the property is gettable aor settable."
         access="public">

      <cfset var metadata = "" />
      <cfset var properties = arrayNew(1) />
      <cfset var index = "" />
         
      <cfif StructKeyExists( variables.instance, "_properties" ) AND IsStruct( variables.instance._properties )>
         <cfreturn variables.instance._properties />
      <cfelse>
         <!--- The first time we get or set a property we need to build a structure of properties --->
         <cfset variables.instance._properties = {} />
         
         <cfset metadata = getMetaData(this)>
         
         <!--- Loop through the component hierarchy --->
         <cfloop condition="structKeyExists(metadata,'properties') OR structKeyExists(metadata,'extends')">
            <cfif structKeyExists(metadata,"properties")>
               <cfset properties = metadata.properties />
               <cfloop array="#properties#" index="index">
                  <cfset variables.instance._properties[index.name] = structNew()/>
                  <cfif IsDefined("index.type")>
                     <cfset variables.instance._properties[index.name].type = index.type/>
                  <cfelse>
                     <cfset variables.instance._properties[index.name].type = "any" />
                  </cfif>
                  <cfif IsDefined("index.gettable")>
                     <cfset variables.instance._properties[index.name].gettable = iif(index.gettable,"true","false")/>
                  <cfelse>
                     <cfset variables.instance._properties[index.name].gettable = "false" />
                  </cfif>
                  <cfif IsDefined("index.settable")>
                     <cfset variables.instance._properties[index.name].settable = iif(index.settable,"true","false")/>
                  <cfelse>
                     <cfset variables.instance._properties[index.name].settable = "false"/>
                  </cfif>
               </cfloop>
            </cfif>
            <!--- Check if we need to go to the next level of hierarchy --->
            <cfif structKeyExists(metadata,"extends")>
               <cfset metadata = metadata.extends>
            <cfelse>
               <cfset metadata = "">
            </cfif>
         </cfloop>
         
         <cfreturn variables.instance._properties />
      </cfif>
         
   </cffunction>

To use it you will need to add the extra attributes to the properties tags:

<cfproperty name="createDate" type="date" gettable="true" settable="false" />
<cfproperty name="createdBy" type="numeric" gettable="true" settable="false" />

Please let me know if you find it useful.

Comments (Comment Moderation is enabled. Your comment will not appear until approved.)
John Whish's Gravatar It's probably worth adding the output="false" attribute to avoid the occasional weirdness. I'll update my post as well :)
# Posted By John Whish | 1/28/09 12:27 PM
Alan Livie's Gravatar I've been using oMM() for a while for getters and setters but without the <cfproperty> tags extra attributes.

This is a great idea and I'll start using this approach until CF9 strikes!

Thanks for sharing.
# Posted By Alan Livie | 1/28/09 8:27 PM
BlogCFC was created by Raymond Camden. This blog is running version 5.8.001.