You can use onMissingMethod() for this.
component name="myComponent" hint="myComponent.cfc"{ function init( struct dynamicProperties={} ){ dynamicProperties.each( function( name, default ){ variables[ name ] = default; } ); return this; } function onMissingMethod( name, args ){ if( name.Left( 3 ) IS "get" ) return get( name ); if( ( name.Left( 3 ) IS "set" ) AND ( args.Len() IS 1 ) ) return set( name, args[ 1 ] ); cfthrow( type="NonExistentMethod", message="The method '#name#' doesn't exist" ); } public any function get( required string accessorName ){ var propertyName = parsePropertyName( accessorName ); if( !variables.KeyExists( propertyName ) ) cfthrow( type="NonExistentProperty", message="The property '#propertyName#' doesn't exist" ); return variables[ propertyName ]; } public void function set( required string accessorName, required any value ){ var propertyName = parsePropertyName( accessorName ); if( !variables.KeyExists( propertyName ) ) cfthrow( type="NonExistentProperty", message="The property '#propertyName#' doesn't exist" ); variables[ propertyName ] = value; } private string function parsePropertyName( accessorName ){ return accessorName.RemoveChars( 1, 3 ); } }
Pass in your property / default name structure and it will โlistenโ for getters / setters that match. Anything that does not result in an exception.
<cfscript> myDynamicProperties = { A: 0, B: 0 }; // this is a struct of names and default values mc = new myComponent( myDynamicProperties ); mc.setA( 100 ); WriteDump( mc.getA() ); // 100 WriteDump( mc.getB() ); // 0 WriteDump( mc.getC() ); // exception </cfscript>
UPDATE 1: the array of property names is replaced with the name name / default value struct as an argument to init to allow the setting of default values.
UPDATE 2: If you want to pass an array of structures containing name / value pairs by default, for example.
dynamicProperties = [ { name: "A", default: 1 }, { name: "B", default: 2 } ];
then the init () method will be:
function init( array dynamicProperties=[] ){ dynamicProperties.each( function( item ){ variables[ item.name ] = item.default; } ); return this; }
UPDATE 3: If you must use tags and <cfloop> to set dynamic properties, then this is all you need in the init method:
<cfloop array="#dynamicProperties#" item="item"> <cfset variables[ item.name ] = item.default> </cfloop>
onMissingMethod will not fire if you try to call dynamic methods as properties like this:
method = mc[ "set#property#" ]; method( value );
Instead, simply create the set() and get() methods in a public component and call them directly:
mc.set( property, value ); mc.get( property );