Calling CFC Methods from Flash
As you have seen, Flash Remoting makes it really easy to create ColdFusion pages that supply information to Flash, or perform whatever other type of processing you want to trigger from movies playing in the Flash Player. Just think of each directory that contains such ColdFusion pages as a service, and of each individual page as a service function.Flash Remoting also makes it possible to use ColdFusion Components to supply information or other server-side processing to Flash. Nearly everything you do on the Flash side of things is exactly the same. The only difference is that each CFC constitutes a service, and each of the CFC's methods constitutes service functions.In other words, you can think of a service as a collection of functions. Whether you want to write those functions as pages in a directory or as methods of a CFC is up to you.Of course, if you go the CFC route, you get all the other benefits of CFCs for free, including automatic documentation and integration with Dreamweaver. But the greatest benefit of going the CFC route is the fact that your CFCs can be used internally by your ColdFusion pages (via the <cfinvoke> tag) or as Web Services, as well as by your Flash applications. See Chapter 22 for details about ColdFusion Components.
NOTE
I am talking about ColdFusion Components, special CFML code files executed on the server. ColdFusion Components are completely different from what Flash calls components; the latter show up in the Components panel in Flash and usually present themselves visually within the player.ColdFusion Components as Services
To demonstrate how easy it is to use ColdFusion Components in your Flash applications, you will now create a CFC that takes the place of the ColdFusion pages used by the Merchandise Browser example in the previous section. The Merchandise Browser calls two ColdFusion pages as service functions: MerchRecordsetProvider.cfm (Listing 26.4) and MerchDetailProvider.cfm (Listing 26.8).Listing 26.9 creates a new ColdFusion Component called MerchProviderCFC which can be used instead. The component exposes two methods called MerchRecordsetProvider() and MerchDetailProvider() that correspond to the two ColdFusion pages already in place.
Listing 26.9. MerchProviderCFC.cfcA CFC That Supplies Data to Flash, ColdFusion, or Other
Chapter 22 for details.If you compare the first <cffunction> block in this listing to the code in Listing 26.4, you will see that it's almost identical. The same query is run to get information about products from the database. The only difference is how the query is returned: instead of returning it specifically to the Flash Player with FLASH.feturn, this code returns it to whatever program is calling the method with the <cfreturn> tag.In other words, the two versions of the code are the same, except that the CFC version isn't coded specifically for Flash, which means you get the bonus of being able to use this method internally within your ColdFusion pages. The access="Remote" attribute means the method can also be accessed as a Web Service.The same goes for the second <cffunction> block; it's nearly identical to Listing 26.8, the ColdFusion page on which it is based. Instead of expecting a parameter called merchID to be passed from the Flash Player specifically, it simply expects that an argument named merchID be passed to the method, whether it's called by Flash, from a ColdFusion page via the <cfinvoke> tag, or by some other means.See Chapter 22 for further discussion of CFC concepts, and for details about <cffunction>, <cfargument>, and <cfreturn>.
<!---
Filename: MerchProviderCFC.cfc
Author: Nate Weiss (NMW)
Purpose:
Creates a ColdFusion Component that supplies data about products
--->
<cfcomponent hint="Provides data about
merchandise records." output="false">
<!--- getMerchList() function --->
<cffunction name="merchRecordsetProvider"
returnType="query" access="remote"
hint="Returns a recordset of all products in
the Merchandise table."
output="false">
<cfset var merchQuery = ">
<!--- Query the database for
merchandise records --->
<cfquery name="merchQuery"
datasource="ows">
SELECT MerchID, MerchName
FROM Merchandise
ORDER BY MerchName
</cfquery>
<!--- Return the query --->
<cfreturn merchQuery>
</cffunction>
<!--- merchDetailProvider() function --->
<cffunction name="merchDetailProvider"
returnType="query" access="remote"
hint="Returns details about a particular
item in the Merchandise table."
output="false">
<!--- MerchID argument (required) --->
<cfargument name="merchID"
type="numeric" required="Yes"
hint="The ID number of the
desired Merchandise record.">
<cfset var merchQuery = ">
<!--- Query the database
for merchandise records --->
<cfquery name="merchQuery"
datasource="ows" maxrows="1">
SELECT MerchID, MerchName,
MerchDescription, ImageNameSmall, MerchPrice
FROM Merchandise
WHERE MerchID = #ARGUMENTS.merchID#
</cfquery>
<!--- Format the MerchPrice column in
the appropriate currency format --->
<!--- (It's easier to do this with
ColdFusion than with ActionScript) --->
<cfset merchQuery.lerchPrice =
lsCurrencyFormat(merchQuery.MerchPrice)>
<!--- Return the query --->
<cfreturn merchQuery>
</cffunction>
</cfcomponent>
Calling CFC Methods
Once the CFC in Listing 26.5) and change this line:
to this:
myService = gateway_conn.getService("ows.23", this);
That's it! You can now publish or test the movie again, and it will behave in exactly the same way it did before.As you can see, to create a service reference to a CFC, you simply refer to the CFC by name (that is, the filename without the .cfc extension). You specify the path to the CFC file's location in exactly the same way as you would with the <cfinvoke> tag, by using dots to separate the folder names in the path (rather than slashes).Once you've created the service reference variable, you can call its service functions (that is, the CFC's methods) by calling each function as a method of the variable, just as you did before. So, to call the MerchRecordsetProvider() method of the CFC in Listing 26.9, you can continue using the following line of code:
myService = gateway_conn.getService("ows.23.MerchProviderCFC", this);
To call the MerchDetailProvider() method of the CFC and provide the MerchID parameter, you can use this line, which is also unchanged from the original version in Listing 26.5:
myService.MerchRecordsetProvider();
Of course, if you were to change the name of this CFC method to getMerchDetails(), say, you would simply change the function call accordingly, like so:
myService.MerchDetailProvider({MerchID:MerchListBox.getValue()});
myService.getMerchDetails({MerchID:MerchListBox.getValue()});
NOTE
You would also need to change the name of the event handler that executes when the data is received. Instead of MerchDetailProvider_Result, you would name it getMerchDetails_Result.Instantiated CFCs
Flash Remoting doesn't provide a mechanism for directly accessing components stored in the APPLICATION or SESSION scopes as discussed in Chapter 23. When you call a method as a Flash service function, you are always calling the method statically, not via an instance of the component.This doesn't mean you have to rule out the idea of instance-based components in your Flash-based applications entirely, however. Chapter 28, "Online Commerce."Chapter 28 from the CD-ROM to the ows/28 folder on your ColdFusion server. If not, please do so now. See Chapter 28 for details on how the ShoppingCart component works internally.In other words, the CFC in Listing 26.10 is a wrapper around the session-based instance of the Shopping Cart component. This means ColdFusion pages can use the cart instance, and you can still expose it to Flash via the wrapper component.
Listing 26.10. CallShoppingCartCFC.cfcUsing a Session-Based CFC Instance
Figure 26.10). Additionally, the Add To Cart button in the detail view for each product now works as expected (it was not operational in the original version).Listing 26.5), except for a small number of additions. The code in the first frame of the new movie is shown in Listing 26.11.
<cfcomponent output="false">
<!--- addItem() function --->
<cffunction name="addItem"
access="remote" output="false" returnType="void"
hint="Adds an item to the session's shopping cart.">
<!--- Required argument: MerchID --->
<cfargument name="merchID"
type="numeric" required="Yes">
<!--- Call the Add() method of the
ShoppingCart CFC --->
<cfinvoke component=
"#SESSION.myShoppingCart#" method="add"
merchID="#ARGUMENTS.merchID#">
</cffunction>
<!--- getItemCount() function --->
<cffunction name="getItemCount"
returnType="numeric" access="Remote"
output="false">
<cfset var cartContents = ">
<cfset var getCount = ">
<!--- Call the List() method of the
ShoppingCart CFC --->
<cfinvoke component="#SESSION.
myShoppingCart#" method="list"
returnVariable="cartContents">
<!--- Use Query-of-Queries to get
the number of items in cart --->
<cfquery dbtype="query" name="getCount">
SELECT SUM(Quantity) AS ItemCount
FROM CartContents
</cfquery>
<!--- Return the total number of items to Flash --->
<cfreturn val(getCount.ItemCount)>
</cffunction>
</cfcomponent>
Listing 26.11. ActionScript Code in the First Frame of MerchBrowserCart.fla
Near the top of the template, a new Flash Remoting service object is created, called cart Service. This object represents the CallShoppingCartCFC component created in Listing 26.10. This is in addition to the myService object already being used to connect to the MerchProviderCFC component from Listing 26.9. I can call CallShoppingCartCFC methods using cartService; I will continue to call the other methods using myService.In addition, several new functions have been added near the bottom of the listing, calledrefreshCart(), GetItemCount_Result(), addSelectedItemToCart(), and AddItem_Result(). Note that refreshCart() is called right away, just before the stop() command.Aside from the code in Listing 26.11, the following code has been added to the Add To Cart button in the DetailUI layer:
// Include support for Flash Remoting Components
#include "NetServices.as"
// uncomment this line when you want to
use the NetConnect debugger
// #include "NetDebug.as"
// --------------------------------------------------
// Handlers for user interaction events
// --------------------------------------------------
// --------------------------------------------------
// Application initialization
// --------------------------------------------------
if (inited == null)
{
// do this code only once
inited = true;
// set the default gateway URL
(this is used only in authoring)
NetServices.setDefaultGatewayUrl
("http://localhost:8501/flashservices/gateway")
// connect to the gateway
gateway_conn = NetServices.
createGatewayConnection();
// get a reference to a service
// In this case, the "service"
is the MerchProviderCFC component
myService = gateway_conn.getService("ows.26.MerchProviderCFC", this);
cartService = gateway_conn.getService
("ows.26.CallShoppingCartCFC", this);
// Call the service function that fills
the ListBox with a list
// of products (from ColdFusion) for
the user to browse through
myService.MerchRecordsetProvider();
}
// This function executes when the
user selects an item in the ListBox
function MerchListBox_Changed() {
// If this is the first time an item
has been selected,
// go straight to the frame that
loads the detail information
if (_currentFrame == 1) {
gotoAndPlay("EndSlideOut");
// Otherwise, go to the frame
that slides the display back in (hides it)
// When it finishes sliding,
it will load the detail information
} else {
gotoAndPlay("StartSlideOut");
}
}
// This function retrieves the
detail information about the selected product.
// It's executed when the last
frame of the movie is reached
// (when the detail view has
finished hiding itself under the product list)
function getSelectedItemDetails() {
myService.MerchDetailProvider(
{MerchID:MerchListBox.getValue()});
}
// --------------------------------------------------
// Handlers for data coming in from server
// --------------------------------------------------
function MerchRecordsetProvider_Result(result) {
// First, remove any existing items from the list box
MerchListBox.removeAll();
//DataGlue.bindFormatStrings (MerchListBox, result,
"#MerchName#", "#MerchID#");
// For each record in the recordset...
for (var i = 0; i < result.getLength(); i++) {
// Use the record variable to refer to
the current row of recordset
var record = result.getItemAt(i);
// Add item to the MerchListBox widget,
which is like a <SELECT> i269
MerchListBox.addItem
(record.MerchName, record.MerchID);
};
}
// This executes when a merchandise
detail record has been received
function MerchDetailProvider_Result(result) {
// The result variable is a recordset
that contains just one row
// The detailRecord variable will
represent the row of data
var detailRecord = result.getItemAt(0);
// Display detail information in text boxes
_root.TitleTextBox.text =
detailRecord.MerchName;
_root.DescriptionTextBox.text =
detailRecord.MerchDescription;
_root.PriceTextBox.text = "Price: " +
detailRecord.MerchPrice;
// If the ImageNameSmall column
contains an image filename, display it
if (detailRecord.ImageNameSmall.length > 0) {
// Load and display the product image
loadMovie("../images/" + detailRecord.
ImageNameSmall, _root.ImageMovie);
// Hide the OWS logo
OWSLogo._visible = false;
// If there is no image file for this record,
display the OWS logo instead
} else {
// Unload any product image that
might already be showing
unloadMovie(_root.ImageMovie);
// Make the OWS logo visible
OWSLogo._visible = true;
}
// Now that the information about
the merchandise has been placed,
// make the display slide back into view,
revealing the information
gotoAndPlay("StartSlideIn");
}
// This function updates the Number
Of Items display for the cart
function refreshCart() {
// Use Flash Remoting to get
the number of items in cart
// When the number has been
retrieved, execution will continue
// in the GetItemCount_Result() function, below
cartService.GetItemCount();
};
// This executes when the number of
items in the cart is received
function GetItemCount_Result(result) {
CartItemCount = "Items: " + result;
};
// This executes when a user uses
the Add To Cart button
function addSelectedItemToCart() {
cartService.AddItem(
{MerchID:MerchListBox.getValue()});
}
// This executes after an
item has been added to the cart
function AddItem_Result(result) {
// Show the new number of items in the cart
refreshCart();
}
// Show the number of items in the user's cart now
refreshCart();
// Stop here, so animation doesn't
occur until user selects a product
stop();
Finally, the following code has been added to the Shopping Cart button at the upper right corner, in a new layer called CartUI:
on (release) {
addSelectedItemToCart();
}
When this version of the movie first appears, the refreshCart() function in Figure 26.10), the addSelectedItemToCart() function is called. Within addSelectedItemToCart(), the cartService.AddItem() service method is called, executing the AddItem() CFC method from Chapter 28) by clicking the Shopping Cart button in the upper right corner. This causes the browser to navigate to the StoreCart.cfm page (from Chapter 28), where the user can check out, remove items, update quantities, or continue shopping.A neat thing about this application is that it proves that both the Flash player and normal ColdFusion pages can use the same SESSION scope. If you make changes in th260 version of the cart, the Flash movie will reflect them, and vice versa. This is because the Shopping Cart CFC, and thus the THIS scope used internally by the component, is maintained in the SESSION scope.
on (release) {
getURL("../28/StoreCart.cfm");
}