Exchanging User Defined Objects Between ColdFusion and Flex

As I've blogged about before I really appreciate how well Flex 2.0 works with ColdFusion MX 7 on the back end. One area I'm learning is how to exchange an object between Flex and CF. The goal being that both CF and Flex will be able to understand the object's class type and access the object's properties. Many of my CF components (for example a PersonDAO) have functions that either require an object of a specific type be sent as an argument and/or return an object of a specific type. If I want to use these same CFCs with Flex, I need Flex and CF to be able to exchange objects of a specific type.

My main reference for this tutorial is an old (in Internet time) blog entry by Ben Forta about automatic CFC - ActionScript conversion between CF and Flex 2.0 (at that time a beta version). I've developed an example of my methodology for exchanging objects between Flex and CF. My example's focus is on the exchange of the object, so it's not a full-scale Flex application.

Let's start with the CF part first. Below is the code for my Person CFC. This is a standard CFC with instance variables (what a Person knows) and mutator/accessor methods for setting and getting the values of the instance variables. I'm assuming you understand how to create CFCs. Note that I've used the cfproperty tag to document the CFC's instance fields (properties). Using the cfproperty tag is required if you want to relate the CFC with an ActionScript class in your Flex application.



<cfcomponent displayName="Person" hint="Class represents a Person">

<!---document each instance variable--->
    <cfproperty name="personID" displayName="Person ID" hint="A unique Person ID is assigned to each record" type="numeric" default="0">
    
    <cfproperty name="firstName" displayName="First Name" hint="Person's first name" type="string" default="">
    
    <cfproperty name="lastName" displayName="Last Name" hint="Person's last name" type="string" default="">
    
    <cfproperty name="phoneNumber" displayName="Phone Number" hint="Person's phone number ( format is (999) 999-9999 )" type="string" default="">
    
    
    
    <!---Initialization area (similar to default constructor)--->
    
    <cfscript>
    
        variables.personID = 0;
        variables.firstName = "";
        variables.lastName = "" ;
        variables.phoneNumber = "";

            
    
</cfscript>
    
    
    <!---init method - can be used like overloaded constructor--->
    
    <cffunction name="init" access="public" returntype="Person" output="no" hint="Set the initial values for the instance variables
    and returns a Person object"
>

    
     <cfargument name="aPersonId" type="numeric" required="false" default="0" />
        
        <cfargument name="aFirstName" type="string" required="false" default="" />
        
        
        <cfargument name="aLastName" type="string" required="false" default="" />
        
        <cfargument name="aPhoneNumber" type="string" required="false" default="" />
        
            <cfscript>
            
             setPersonID(arguments.aPersonID) ;
            
             setFirstName(arguments.aFirstName);
            
             setLastName(arguments.aLastName) ;
            
             setPhoneNumber(arguments.aPhoneNumber);
                
                
            
</cfscript>
            
            <cfreturn this />
        
    
    </cffunction>
    

    
    <!---mutator methods--->
    
    
    
    <cffunction name="setPersonID" displayname="Set Person ID" access="public" returntype="void" output="no">
    
        <cfargument name="aPersonID" type="numeric" required="true">
        
        
        
            <cfset variables.personID = arguments.aPersonID>
        
    
    
    </cffunction>
    
    <cffunction name="setFirstName" displayname="Set First Name" access="public" returntype="void" output="no">
    
        <cfargument name="aFirstName" type="string" required="true">
        
        
        
            <cfset variables.firstName = arguments.aFirstName>
        
        
    
    </cffunction>
    
    
    
    <cffunction name="setLastName" displayname="Set Last Name" access="public" returntype="void" output="no">
    
        <cfargument name="aLastName" type="string" required="true">
        
    
        
            <cfset variables.lastName = arguments.aLastName>
        

    
    </cffunction>
    
        
    <cffunction name="setPhoneNumber" access="public" returntype="void" output="no">
    
        <cfargument name="aPhoneNumber" type="string" required="true">
        
        
        
            <cfset variables.phoneNumber = Arguments.aPhoneNumber>
        

    
    </cffunction>
    
    
    

    
    <!---accessor methods--->
    
    <cffunction name="getPersonID" access="public" returntype="numeric" output="no">
    
        
        
            <cfreturn variables.personID>

    

    </cffunction>
    
    
    
    <cffunction name="getFirstName" access="public" returntype="string" output="no">
    
        
        
            <cfreturn variables.firstName>

    

    </cffunction>
    

    
    <cffunction name="getLastName" access="public" returntype="string" output="no">
    
    
        
            <cfreturn variables.lastName>


    </cffunction>
    
    
    
    <cffunction name="getPhoneNumber" access="public" returntype="string" output="no">
    
        
        
            <cfreturn variables.phoneNumber>


    </cffunction>
    
    
    
    
        
    
</cfcomponent>


In addition to the Person CFC I have a PersonDAO (data access object) CFC. There are only two methods defined in this CFC (read and update) to demonstrate that feasibility of my example. The read method gets a specific record from the database table and updates a Person object's instance fields with the record's column values. The update method uses the state of a Person object to update a specific record in the database table. Note that both methods have remote access so that my Flex app may call these methods.





<cfcomponent displayname="PersonDAO" output="false" hint="Person DAO">

<!---document each instance variable--->
    <cfproperty name="dsn" displayname="Datasource Name" hint="Datasource name used to connect CF to the database"
    type="string">

    
    <!---pseudo constructor--->
    
    <cfscript>
    
        variables.dsn = "BrucePresenter" ;
        
    
</cfscript>
    
    <cffunction name="init" access="public" output="false" returntype="PersonDAO" hint="Constructor for this CFC">
        <!--- take in the datasource name as an argument and set it to the variables scope so it's available
                throughout the CFC --->

        <cfargument name="dsn" type="string" required="true" />
        <cfset variables.dsn = arguments.dsn />
        
        <!--- return the object itself --->
        <cfreturn this />
    </cffunction>
    
    
        
    
    <!--- READ: populates a Person object using info from the database --->
    <cffunction name="read" access="remote" output="false" returntype="Person"
            hint="Finds the record using personID of the person object passed in and then updates the person object's state
             using the fields returned by the query. If no record is returned or more than one record is returned
             throws a Person.NotFound exception "
>

            
        <cfargument name="aPerson" type="Person" required="true" />
        
        <!--- var scope everything! --->
        <cfset var getPerson = "" />
        
        <cfquery name="getPerson" datasource="#variables.dsn#">
            SELECT [presenterID] as personID, lastName, firstName, phone
            FROM presenters
            WHERE [presenterID] = <cfqueryparam value="#arguments.aPerson.getPersonId()#" cfsqltype="cf_sql_integer" />
        </cfquery>
        
        <cfif getPerson.recordcount neq 1>
            <cfthrow type="Person.NotFound" message="I didnt find a person with personID: #arguments.aPerson.getPersonID()#">
        </cfif>
        
        <!--- if we got a record back, populate the object --->
        <cfif getPerson.RecordCount EQ 1>
        
         <!---update the state of the person object--->
            <cfscript>
            
             aPerson.setLastName( getPerson.lastName ) ;
             aPerson.setFirstName( getPerson.firstName ) ;
             aPerson.setPhoneNumber( getPerson.phone ) ;
            
            
</cfscript>
            
             <cfreturn aPerson ><!---this is needed for Flex 2.0 since the Person oject passed to this method by the Flex app is apparently not passed by reference--->
            
        </cfif>
        
    </cffunction><!---end read method--->
    
    
    <!--- UPDATE: updates an existing record in the database using the data in the object that's passed in --->
    <cffunction name="update" access="remote" output="false" returntype="struct"
            hint="Updates an existing record in the database and returns a struct containing a boolean and a string">

        <cfargument name="aPerson" type="Person" required="true" />
        
        
        <cfset var updatePerson = "" />
        <cfset var results = StructNew() />
        <cfset results.success = true />
        <cfset results.message = "The person was updated successfully." />
        
        <cftry>
        
            <cftransaction action="begin"> <!---we need to insert data into two tables--->
            
            <cfquery name="updatePerson" datasource="#variables.dsn#">
                UPDATE     Presenters
                SET     firstname = <cfqueryparam value="#arguments.aPerson.getFirstName()#" cfsqltype="cf_sql_varchar" />,
                lastname = <cfqueryparam value="#arguments.aPerson.getLastName()#" cfsqltype="cf_sql_varchar" />,
                phone = <cfqueryparam value="#arguments.aPerson.getPhoneNumber()#" cfsqltype="cf_sql_varchar" />
                
                        
                WHERE     presenterID = <cfqueryparam value="#arguments.aPerson.getPersonId()#" cfsqltype="cf_sql_integer" />
            </cfquery>
            
            <!---update tblPeopleAddresses--->
            
            <cftransaction action="commit" />
            
            </cftransaction>
            
            <cfcatch type="database">
                <cfset results.success = false />
                <cfset results.message = "A database error occurred: #CFCATCH.Detail#" />
                <cftransaction action="rollback" />
            </cfcatch>
            
            
            
        </cftry>
        
        <cfreturn results />
        
    </cffunction><!--end update method--->
    
    
    
</cfcomponent>


Note that the read method returns an object of type Person. Normally my read method just updates the Person parameter object. Because of how objects are passed in CF, this technique also updates the Person object that was sent to the method. However, this methodology will not work for a Person object passed to the method from Flex. Whatever the read method does internally to the Person object parameter doesn't effect the Person object that is back in the Flex application. So my read method has to return back to the Flex application a Person object.

In my Flex application (right click on the Flex app to view the source) I have an ActionScript 3.0 class named Person also. This ActionScript class is the "twin" of my Person CFC. These lines of code in the ActionScript class:

[Bindable]
[RemoteClass(alias="brucephillips.flex.cfobject.Person")]

tell my Flex application that the CFC "twin" of this class is located on the path specified for the alias value. Also notice that each of my ActionScript Person's instance fields (properties) are declared public and the fields are listed in the same order and have the same type as declared in my Person CFC (as specified in the cfqueryparam tags). When I made the fields private, the exchange of the field values between Flex and CF did not work.

When the Flex application first loads, it creates a Person object using the ActionScript class and then updates the Person object with a hard-coded personID value (15844). The Flex app then passes this Person object to the CFC read method. The read method pulls from the database the record with the matching personID, updates the fields of the Person object, and returns the Person object back.

The Flex app receives the Person object back (see method handleReadResult). Since the form fields are bound to the ActionScript's Person object the form fields are populated with the values from the Person object. The Update button is enabled and the user can now change the values in the form fields.

When the user clicks on the Update button, the ActionScript Person object's fields are updated with the form field values (note I don't do any data validation - NOT A GOOD PROCEDURE). Then the Person object is passed to the update CFC method. The update CFC method uses the values of the Person object's instance fields to update the table columns for that specific record. The CFC update method returns a structure with a message and Flex places the message's value into the updateMsg String variable (see method handleUpdateResult).

This example demonstrates how we can exchange objects of user defined data types (in this case class Person) between Flex and CF. Existing CFC that are used to create specific types of objects and/or require specific types of objects as arguments and/or return specific types of objects can be leveraged in your Flex applications by applying this methodology.

Comments (Comment Moderation is enabled. Your comment will not appear until approved.)
GREAT Post... I too am learning this... I've been on a Flex Project, and been working with flex on a daily basis (full time 10 hours a day type stuff) for a few months, and i've lost some basic coldfusion skills... Not with cfc's of course, but with regular page based architecture... and on friday, i got stuck back on a project that was page based again.... and i realized i have severely tooken a step back in page based programming, but it only took a couple of hours of fumblying around to get it right back... remembering what i have access to and what i don't was the challenge... form submission and things like that.... anyway.... the cfc wizard comes in handy and first for this, because i'm making all of my cfc's to work with page based architecture, to work with flex as well... NO CODE REWRITING! I will soon talk about form reuse on my blog thanks to this great flex venture, it has taught me a great web 2.0 way to do things, and now i can take some of those ideas back with me, and kind of re engineer the way i was doing things...
# Posted By Axel | 2/25/07 11:38 PM
Great article - very helpful. I've had some difficluty getting valueobjects from CF to maintain their custom type when transferred to flex. I had to reconstruct and re-type them inside flex. I will try your method
# Posted By Ged Mc | 8/10/07 3:52 AM
Thank You for this post! I forgot to alias my CFC in my actionscript class and it was beating me up...

Your post saved my sanity! :)
# Posted By Seth Bienek | 8/19/07 7:47 PM
Great post!
# Posted By Johan | 2/17/08 1:54 PM
Bruce, Thanks for the great article. I am developing a similar system right now using user objects, but for some reason my handleReadResult event will not 'cast' to my user object type. If I dump the event.result using
trace(ObjectUtil.toString(event.result));
within the handler, I see all of the user info (first name, last name, etc) that I expect, but when do
loggedInUser = event.result as myUserObject;

loggedInUser (of type myUserObject) comes back null.

Any idea what I may be doing wrong?
ps. Thanks for all the effort you put into your site, it has been invaluable as I battle my way through Flex!
# Posted By Brian | 4/3/08 2:05 PM
Brian - check my other articles on Flex - ColdFusion connectivity. There are several issues that can cause the Object being returned by ColdFusion to not be recognized correctly on the Flex end.

For instance see: http://tinyurl.com/23k8cz
# Posted By Bruce | 4/3/08 6:30 PM
BlogCFC was created by Raymond Camden. This blog is running version 5.9.1.002. Contact Blog Owner