Writing the ColdFusion CFCs



When you create your ColdFusion CFCs, you can do one of the following:

  • Create an assembler CFC and a Value Object CFC.

  • Create an assembler CFC, a Data Access Object (DAO) CFC, and a Value Object CFC.

You place the database manipulation functionality directly in the methods in the assembler CFC and create a Value Object CFC, which is a CFC that contains property definitions and related get and set methods.

To separate the lower level database functionality from the high-level Flex assembler operations, you create a Data Access Object (DAO) CFC that contains the lower level database functionality. Using this approach, which is the Bean/DAO methodology, requires that you place the fill, get, sync, and count methods in the assembler CFC. The methods in the assembler CFC call methods in the DAO CFC that perform the lower level database functions such as retrieving records. The DAO CFC creates Value Objects, which are CFCs that contain the values. A Value Object is essentially a row in the result set.

The LiveCycle Data Management Service recognizes the methods: fill, get, sync, and count. The fill method retrieves records from a database and populates an array with the records. The get method retrieves a specific record. The sync method lets you keep track of synchronization conflicts by accepting a change list, which is an array of change objects. The count method returns a number that indicates how many records are in a result set. To perform any of these database tasks, the Flex application calls the appropriate fill, get, sync, or count method in the assembler CFC. You can also use a fillContains method, which checks whether to update the results of a fill. For more information, see Managing fills.

Creating the fill method

The fill method retrieves records from a database and populates an array with the records. If you use the Bean/DAO methodology, you create the lower level read method separately in the DAO CFC.

The fill method returns the results of a read operation. In the fill method, you create an array to hold the results of the read, and then return the results of the read operation. The essential elements of a fill method appear as follows:

<cffunction name="fill" output="no" returntype="samples.contact.Contact[]" access="remote"> 
    <cfreturn variables.dao.read()> 
</cffunction>

You can return a Value Object CFC, a query, or an array of CFML structures. Using a query instead of a Value Object CFC may improve performance. However, ColdFusion cannot handle nested results sets when you use a query. For example, if one of the CFC properties you are returning from the fill method was populated with another complex type such as another CFC type, ColdFusion cannot automatically convert a column in the query to an object with a custom type. In this case, you return an array of CFCs, and the fill method or the read method in the DAO CFC constructs the correct object.

You can use structures wherever you currently create a ColdFusion component in the Assembler. However, you still receive CFC Value Objects from Flex. For example, the Change Objects that you receive in the sync method contain CFCs, assuming that you have a remote alias defined in the ActionScript type.

You can create Value Object CFCs in the get method. However, using the structure functionality, you can create and return a structure instead of a CFC, because the structures are translated in the same way as CFCs. You can also return an array of structures from the fill method instead of an array of CFCs, for example, if you have to do processing on your data and working with CFCs isn't fast enough. Generally, structures are faster than CFCs. You also use structures when a member of the result object is a complex object. In this case, you create another structure as the value of that key and provide the __type__ key for it.

You specify the returntype of the fill method as a Value Object CFC, a query, or an array:

  1. Value Object:

    <cffunction name="fill" output="no" 
        returntype="samples.contact.Contact[]" access="remote">
  2. Query:

    <cffunction name="fill" output="no"  
        returntype="query" access="remote">
  3. Array of structures:

    <cffunction name="fill" output="no"  
        returntype="array" access="remote">

In addition to specifying the returntype of the fill function depending on whether you are using Value Objects, a query, or an array of structures, you also do the following in the lower level read function:

  • Specify the returntype of the read function as the Value Object CFC, a query, or an array, for example:

    • <cffunction name="read" output="false" access="public" returntype="samples.contact.Contact[]">

    • <cffunction name="read" output="false" access="public" returntype="query">

    • <cffunction name="read" output="false" access="public" returntype="array">

  • If you are using Value Objects:

    • Create the array to contain the Value Objects, as follows:

      <cfset var ret = ArrayNew(1)>
    • Loop through the query to create each Value Object based on each row of the query, for example:

      <cfloop query="qRead"> 
          <cfscript> 
              obj = createObject("component", 
                  "samples.contact.Contact").init(); 
              obj.setcontactId(qRead.contactId); 
              obj.setfirstName(qRead.firstName); 
              obj.setlastName(qRead.lastName); 
              obj.setaddress(qRead.address); 
              obj.setcity(qRead.city); 
              obj.setstate(qRead.state); 
              obj.setzip(qRead.zip); 
              obj.setphone(qRead.phone); 
              ArrayAppend(ret, obj); 
          </cfscript> 
      </cfloop>
  • If you are using a query:

    • Ensure that you configured the destination with the row type for the destination so that ColdFusion correctly labels each rows in the query with the corresponding ActionScript type. Use the query-row-type element, which is in the metadata section of the destination.

    • Specify the following in the fill method:

      <cffunction name="fill" output="no" returntype="query"  
          access="remote"> 
          <cfargument name="param" type="string" required="no"> 
              <cfquery name="myQuery" .> 
              </cfquery> 
              <!--- Return the result ---> 
          <cfreturn myQuery> 
      </cffunction>
    • If you are using a DAO CFC, edit the read method to return a query instead of an array of CFCs.

    • Ensure that the query column names match the case of the properties in the ActionScript object. Use the property-case settings in the destination to do so. Set the force-query-lowercase element to false so that ColdFusion converts all column names to lowercase.

  • If you are using an array of structures:

    • Create the array to contain the Value Objects, as follows:

      <cfset var ret = ArrayNew(1)>
    • Loop through the query to create the structure that contains the results of the query, for example:

      <cfloop query="qRead"> 
          <cfscript> 
              stContact = structNew(); 
              stContact["__type__"] = "samples.contact.Contact"; 
              stContact["contactId"] = qRead.contactId; 
              stContact["firstName"] = qRead.firstName; 
              stContact["lastName"] = qRead.lastName; 
              stContact["address"] = qRead.address; 
              stContact["city"] = qRead.city; 
              stContact["state"] = qRead.state; 
              stContact["zip"] = qRead.zip; 
              stContact["phone"] = qRead.phone; 
              ArrayAppend(ret, duplicate(stContact)); 
          </cfscript> 
      </cfloop>
    • Use the __type__ structure element to specify that the Value Object CFC is the type, for example:

      stContact["__type_"] = "samples.contact.Contact";
    • Use the associative array syntax, for example, contact["firstName"] to ensure that you match the case of the ActionScript property. If you use the other syntax, for example, contact.firstName="Joan", ColdFusion makes the key name uppercase.

Managing fills

To determine whether to refresh a fill result after an item is created or updated, you include a fillContains method in the assembler and set both use-fill-contains and auto-refresh to true in the fill-method section of the data-management-config.xml file. The following example shows a fill-method section:

<fill-method> 
    <use-fill-contains>true</use-fill-contains> 
    <auto-refresh>true</auto-refresh>  
    <ordered>false</ordered>  
</fill-method> 

In this example, ordered is set to false because the fill result is not sorted by any criteria. However, if the fill result is sorted, you set ordered to true. When an item changes in a fill result that is ordered, refresh the entire fill result.

The fillContains method tells the Flex application whether it is necessary to run the fill again after an item in the fill result has changed. The fillCcontains method returns a value that indicates how the fill be treated for that change. When the fillContains method returns true, the fill is executed after a create or update operation.

The following example shows the fillContains method signature:

<cffunction name="fillContains" output="no" returnType="boolean" access="remote"> 
     <cfargument name="fillArgs" type="array" required="yes"> 
     <cfargument name="item" type="[CFC type object]" required="yes"> 
     <cfargument name="isCreate" type="boolean" required="yes">

The fillContains method has the following arguments:

  • fillArgs is a list of arguments to pass to the fill method.

  • item is the record to check to determine if it is in the result set.

  • isCreate indicates whether the record is new.

A sample fillContains method, which determines whether the fill arguments (part of the first or last name) are in the Contact item passed to the function, is as follows:

<cffunction name="fillContains" output="no" returnType="boolean"access="remote"> 
    <cfargument name="fillArgs" type="array" required="yes"> 
    <cfargument name="item" type="samples.contact.Contact" required="yes"> 
    <cfargument name="isCreate" type="boolean" required="yes"> 
 
<cfif ArrayLen(fillArgs) EQ 0> 
<!--- This is the everything fill. ---> 
<cfreturn true> 
<cfelseif ArrayLen(fillArgs) EQ 1> 
<!--- This is a search fill. ---> 
    <cfset search = fillArgs[1]> 
    <cfset first = item.getFirstName()> 
    <cfset last = item.getLastName()> 
    <!--- If the first or last name contains the search string, ---> 
    <cfif (FindNoCase(search, first) NEQ 0) OR (FindNoCase(search, last)  
        NEQ 0)> 
<!--- this record is in the fill. ---> 
<cfreturn true> 
<cfelse> 
<!--- this record is NOT in the fill. ---> 
<cfreturn false> 
</cfif> 
</cfif> 
 
<!--- By default, do the fill.---> 
<cfreturn true> 
</cffunction>

If you are running LiveCycle Data Services ES locally, you can determine whether a fill operation is a refresh or a client triggered fill. You do so by calling the DataServiceTransaction.getCurrentDataServiceTransaction().isRefill() method in your ColdFusion application as follows:

<cfscript> 
dst = CreateObject("java", "flex.data.DataServiceTransaction"); 
t = dst.getCurrentDataServiceTransaction(); 
isRefill = t.isRefill(); 
</cfscript>

This does not work over RMI when ColdFusion and Flex are not in the same web application.

Creating the get method

The get method retrieves a specific record. The get method calls the lower level read method. If you use the Bean/DAO methodology, as described in Writing the ColdFusion CFCs, you create the lower level read method separately in the DAO CFC.

The following example shows the essential elements of a get method:

<cffunction name="get" output="no" returnType="samples.contact.Contact" access="remote"> 
    <cfargument name="uid" type="struct" required="yes"> 
    <cfset key = uid.contactId> 
    <cfset ret=variables.dao.read(id=key)> 
    <cfreturn ret[1]> 
</cffunction>

The returntype of a get method can be any of the following:

  • The Value Object CFC

  • Any

  • An array

Creating the sync method

The sync method lets you keep track of synchronization conflicts by accepting a change list, which is an array of change objects. In the sync method, you pass in an array of changes, loop over the array and apply the changes, and then return the change objects, as follows:

<cffunction name="sync" output="no" returnType="array" access="remote"> 
    <cfargument name="changes" type="array" required="yes"> 
 
    <!-- Create the array for the returned changes. --> 
    <cfset var newchanges=ArrayNew(1)> 
 
    <!-- Loop over the changes and apply them. ---> 
    <cfloop from="1" to="#ArrayLen(changes)#" index="i" > 
        <cfset co = changes[i]> 
        <cfif co.isCreate()> 
            <cfset x = doCreate(co)> 
        <cfelseif co.isUpdate()> 
            <cfset x = doUpdate(co)> 
        <cfelseif co.isDelete()> 
            <cfset x = doDelete(co)> 
        </cfif> 
        <cfset ArrayAppend(newchanges, x)> 
    </cfloop> 
 
    <!-- Return the change objects, which indicate success or failure. ---> 
    <cfreturn newchanges> 
</cffunction>

Creating the count method

The count method returns a number that indicates how many records are in a result set. If you use the Bean/DAO methodology, as described in Writing the ColdFusion CFCs, you create the lower level count method separately in the DAO CFC.

The count method contains the following essential elements, without any error handling:

<cffunction name="count" output="no" returntype="Numeric" access="remote"> 
    <cfargument name="param" type="string" required="no"> 
    <cfreturn variables.dao.count()> 
</cffunction>

This count method calls a different count method in the DAO CFC, which contains the following essential elements, without any error handling:

<cffunction name="count" output="false" access="public" returntype="Numeric"> 
    <cfargument name="id" required="false"> 
    <cfargument name="param" required="false"> 
    <cfset var qRead=""> 
 
    <cfquery name="qRead" datasource="FDSCFCONTACT"> 
        select COUNT(*) as totalRecords 
        from Contact 
    </cfquery> 
 
    <cfreturn qRead.totalRecords> 
</cffunction>