Question

I have two CFC's which are Beans with DAO functions. I have set up inheritance where the child cfc extends the parent.

Both objects have identical structures; Functions: init, read, etc. and Properties: ID, etc.

When I create the child object, I pass an ID, which reads the data, getting the foreign key parentID for the parent and then Super.init() is called with appropriate parameters.

My unexpected results are: Both the ID for the child and the parentID are the same and are the parentID value when the object is returned. The childs variables.ID is overwritten after the super call. I assume that the variables scope is accessible by both, so when parent sets variables.ID it overwrites the child variables.ID. Can this be avoided without naming IDs differently?

The read function of the parent object does not appear to execute. If I rename the parents read function 'read2' for example, the function executes. I also suspect that the functions reside in a shared scope and therefore the childs read function is executing.

Is there any way to maintain the same cfc structures and get this functionality to work as expected?

Thanks in advance.

<cfcomponent accessors="true" extends="Custom" output="false">
<cfproperty name="ID" type="numeric" />
<cfproperty name="listID" type="numeric" />
<cfproperty name="customfieldID" type="numeric" />
<cfscript>
    variables.dsn = '';
</cfscript>

<cffunction name="init" access="public" output="false" returntype="ListCustom">
    <cfargument name="dsn" type="string" required="true" />
    <cfargument name="ID" type="numeric" required="true" />
    <cfargument name="listID" type="numeric" required="false" default="0" />
    <cfargument name="customFieldID" type="numeric" required="false" default="0" />
    <cfscript>
        variables.dsn = arguments.dsn;
        variables.ID = arguments.ID;
        variables.listID = arguments.listID;
        variables.customFieldID = arguments.customFieldID;
        if (variables.ID){
            read();
            if (variables.customFieldID){
                Super.init(dsn=variables.dsn,ID=variables.customfieldID);
            }
        }
    </cfscript>
    <cfreturn this />
</cffunction>

<cffunction name="read" access="private" output="false" returntype="void">
    <cfquery name="local.q" datasource="#variables.dsn#">
        SELECT customfieldID, listID
        FROM listCustomFields
        WHERE ID = <cfqueryparam value="#variables.ID#" cfsqltype="cf_sql_integer">
    </cfquery>
    <cfif local.q.recordcount>
        <cfset variables.listID = local.q.listID />
        <cfset variables.customFieldID = local.q.customFieldID />
    </cfif>
</cffunction>

<cfcomponent accessors="true" output="false">

<cfproperty name="ID" type="numeric" />
<cfproperty name="fieldName" type="string" />

<cfscript>
    variables.dsn = '';
</cfscript>

<cffunction name="init" access="public" output="false" returntype="Custom">
    <cfargument name="dsn" type="string" required="true" />
    <cfargument name="ID" type="numeric" required="true" />
    <cfargument name="fieldName" type="string" required="false" default="" />
    <cfscript>
        variables.dsn = arguments.dsn;
        variables.ID = arguments.ID;
        variables.fieldName = arguments.fieldName;
        if (variables.ID){
            read();
        }
    </cfscript>
    <cfreturn this />
</cffunction>

<cffunction name="read" access="private" output="false" returntype="void">
    <cfquery name="local.q" datasource="#variables.dsn#">
        SELECT fieldName
        FROM CustomField
        WHERE ID = <cfqueryparam value="#variables.ID#" cfsqltype="cf_sql_integer">
    </cfquery>
    <cfif local.q.recordcount>
        <cfset variables.fieldName = local.q.fieldName />
    </cfif>
</cffunction>

Was it helpful?

Solution 2

First, both garygilbert and KRC were helpful in pointing me in the right direction. Thank you.

I gathered the following info: Both the child and parent will share the same variables scope, so when assigning a value to the variables.ID in super, the child variables.ID value was overwritten.

Similarly, the child function read takes precedence over the parent read function due to OO function overwriting.

My adjusted solution: Set a temporary ID that is the child id prior to calling Super functions. Being that if I pass the ID to the init function, the read function is called automatically, I wanted to avoid doing this in the parent object because it would still call the child read. I call Super.init(dsn) to initialize, then then Super.setID(ID), and finally Super.read(). After the super calls, I restore the initial child ID.

        <cfscript>
        variables.dsn = arguments.dsn;
        variables.ID = arguments.ID;
        variables.listID = arguments.listID;
        variables.customFieldID = arguments.customFieldID;
        if (variables.ID){
            read();
            if (variables.customFieldID){
                variables.tempID = variables.ID;
                Super.init(dsn=variables.dsn);
                Super.setID(variables.customfieldID);
                Super.read();
                variables.ID = variables.tempID;
            }
        }
    </cfscript>

OTHER TIPS

Since you are extending the object the variable scope for both the parent and the child are shared so variables.id in the parent is the same variables.id in the child, and both the child and the parent can access or overwrite what is stored there.

Also with an extend the parent function read will never be called unless the child function read explicitly calls it with super.read().

Also unless you have setup your application and your components to use ORM the cfproperty tags are only good for webservices.

I think the reason why this is happening is because of how CF scopes cfproperty values in the VARIABLES scope of the entire object instead of in an exclusive scope to the CFC (like the generally-accepted VARIABLES.instance struct practice).

The VARIABLES scope is shared across your object, including it's inherited parent, so by naming the ID value in both parent and child, you're actually just declaring it twice instead of declaring two separate variables. Then, when you pass the ID to your super.init(), it overwrites your ID value to your child ID to the ID value you pass into the parent bean, causing your result. If you want to see this in action a different way, try creating a test function in your child that shares the name of a variable in your parent. Assign that variable a value in your super.init(), and watch your child function disappear from your object.

So to answer, I don't think you can fix the problem without doing something you don't want to do. You'll need to use a different name for the parent ID, or to not use the built-in accessor functionality and manually write all of your getter functions, setting them in a scope exclusive to the CFC.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top