Using CFAJAXPROXY to Check Yourself Before You Wreck Yourself!

Many web apps that are used for CRUD activities (INSERT, UPDATE and DELETE statements) employ tables that simply have what is often referred to as a manufactured key. Most people more commonly know this as an auto-increment or auto-numbered identity style of primary key and it is typically stored as a data type that's an integer of some sort. This is great until you're working on something a bit bigger, say a business app, and you want the key to a subsidiary table to hold meaning to the end user throughout the app. Perhaps you're setting up location codes or category codes for instance, the value 5 will probably not mean a lot to an end user when filtering inventory reports by location code and it unfortunately forces the user to perform mental lookups to remember that 5 truly means a location of 'Chicago'.

So let's say that you make the decision to structure lookup tables using user defined key values - would you know how to use CF to help maintain referential integrity in your database while not endlessly frustrating your end users?

Take into account that when you're working with a manufactured primary key you can perform an INSERT and simply move on - clean and easy - which is why most web apps work using manufactured keys. If you want to use a table where users define the key values then you will most definitely add a level of complexity to the simple task of inserting a record. Now you'll have to add logic to check that a user's new key value doesn't already exist in the table before you try to insert their newly created record. This type of primary key setup will also affect updates as you'll now have to compare the key of an existing record against the records that are already in the table to make sure that you're not allowing the user to change a value like 'Chicago' to 'Indianapolis' only to return an error because there is already a key value of 'Indianapolis' in the table.

A great way to gracefully handle these types of scenarios is by using CFAJAXPROXY to call functions which check for the existence of a key before trying to INSERT or UPDATE your table.

For example, we have a table named Warehouse which holds Warehouse Code values. There are two cfc methods that work together to maintain uniqueness in the table while avoiding nasty errors for our user on the client side.

Our first function is a simple query that gets a Warehouse table record...

view plain print about
1<cffunction name="getWarehouse" access="remote" returntype="query" hint="Get a single warehouse record">
2 <cfargument name="warehouseCode" type="string" required="yes">
3 <cfquery name="qryWarehouse" datasource="#REQUEST.ds1#">
4 SELECT WarehouseCode,
5 ShortDesc,
6 Volume,
7 FacilityName,
8 Address,
9 Address2,
10 City,
11 StateCode,
12 PostalCode,
13 CountryCode,
14 Phone,
15 Phone2,
16 Fax,
17 Fax2
18 FROM Warehouse
19 WHERE WarehouseCode = <cfqueryparam cfsqltype="cf_sql_varchar" value="#ARGUMENTS.warehouseCode#">
20 </cfquery>
21 <cfreturn qryWarehouse>
22</cffunction>

Our second function in warehouse.cfc is responsible for checking for the existence of a Warehouse and we will use checkForExistingWarehouse in conjunction with getWarehouse before we allow a user to execute an INSERT or UPDATE statement against our table.

view plain print about
1<cffunction name="checkForExistingWarehouse" access="remote" returntype="boolean" hint="Check for the existence of a warehouse">
2 <cfargument name="warehouseCode" type="string" required="yes">
3 <cfinvoke method="getWarehouse" warehouseCode="#ARGUMENTS.warehouseCode#" returnvariable="qryWarehouse">
4 <cfif qryWarehouse.recordCount EQ 0>
5 <cfset warehouseExists = false>
6 <cfelse>
7 <cfset warehouseExists = true>
8 </cfif>
9 <cfreturn warehouseExists>
10</cffunction>

On the client side we will make use of CFAJAXPROXY to remotely call checkForExistingWarehouse as illustrated below...

view plain print about
1<cfajaxproxy cfc="#REQUEST.cfcDir#.warehouse" jsclassname="proxyWarehouse">
2
3<html xmlns="http://www.w3.org/1999/xhtml">
4<head>
5<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
6<title><cfoutput>#REQUEST.titleText#</cfoutput></title>
7<link rel="stylesheet" href="css/main.css" type="text/css" />
8
9<script>
10
11    function errorHandler(code,msg) {
12        alert('Error ' + code + ': ' + msg);
13    }
14    
15    function validateFormData() {
16        var dataIsValid = _CF_checkeditWarehouse(editWarehouse);
17        if (dataIsValid == true) {
18            var runMode = ColdFusion.getElementValue('runMode', 'editWarehouse', 'value');
19            var warehouseCode = ColdFusion.getElementValue('warehouseCode', 'editWarehouse','value');    
20            var originalWarehouseCode = ColdFusion.getElementValue('originalWarehouseCode', 'editWarehouse', 'value');
21            if (runMode == 'Create' || (runMode == 'Update' && warehouseCode.toLowerCase() != originalWarehouseCode.toLowerCase())) {
22                checkForWarehouse();
23            }
24            else {
25                submitForm();
26            }
27        }    
28    }
29    
30    function checkForWarehouse() {
31        var warehouseCode = ColdFusion.getElementValue('warehouseCode', 'editWarehouse','value');        
32        proxyCheckForWarehouse = new proxyWarehouse();
33        proxyCheckForWarehouse.setErrorHandler(errorHandler);
34        proxyCheckForWarehouse.setCallbackHandler(checkForWarehouseCallback);
35        proxyCheckForWarehouse.checkForExistingWarehouse(warehouseCode);
36    }
37    
38    function checkForWarehouseCallback(callbackMsg) {
39        //alert(callbackMsg);
40        if (callbackMsg == true) {
41            alert('The Warehouse already exists.\nPlease enter a unique Warehouse Code.');
42        }
43        else {
44            submitForm();
45        }            
46    }
47    
48    submitForm = function() {
49        ColdFusion.Ajax.submitForm('editWarehouse', 'formprocs/proceditwarehouse.cfm', submitCallback, errorHandler);        
50    }
51    
52    function submitCallback(callbackMsg) {
53        var runMode = ColdFusion.getElementValue('runMode', 'editWarehouse', 'value');
54        if (runMode == 'Update') {
55            var savedMsg = 'The Warehouse has been saved.';
56        }
57        else if (runMode == 'Create') {
58            var savedMsg = 'The Warehouse has been saved.\nAfter you have created Locations, Zones and Cubes for this Warehouse\nyou can configure Default Staging Codes for Receiving Inventory into this Warehouse.';
59        }
60        alert(savedMsg);
61        jumpToWarehouseList();
62    }
63    
64    function jumpToWarehouseList () {
65        window.location = 'warehouselist.cfm';
66    }
67    
68
69</script>
70
71</head>

There are a few important concepts to cover here but I'll stick to the basics. First, recognize that I'm using some FORM variables to set the original value of the primary key, WarehouseCode in this case, if we're allowing a user to UPDATE an existing record. You'll notice in the validateFormData js function that if our run mode is Create then we automatically must check for the existence of the key value that the user is trying to create. If we're doing an Update then we only check if the original value is not the same as the current value of the cfinput control named warehouseCode. This is simple to do using the ColdFusion.getElementValue function as shown above.

So if we encounter either of these scenarios then we simply call checkForExistingWarehouse using an instance of proxyWarehouse and it will in turn take advantage of getWarehouse. If getWarehouse returns a record then we've got a potential collision and checkForExistingWarehouse will return a true and we can notify our user that they need to enter a unique value. If not then we submit the form and live happily ever after.

Comments
monster energy hats, Rockstar hat, Supreme hat,'s Gravatar It's so lucky for me to find your blog! So shocking and great!
In the Internet ,it have more and more widely.So we could communicate with each other about something in prowantgo.http://www.prowantgo.net/
new era caps, new era hats, Crooks&castles hat's Gravatar It's so lucky for me to find your blog! So shocking and great!
In the Internet ,it have more and more widely.So we could communicate with each other about something in prowantgo.http://www.prowantgo.net/
ciphone's Gravatar Hello!everyone!iphone is so expensive.so I like <a href=" www.efox-shop.com/index.php/cPath/5_35
" rel="nofollow"> Ciphone </a>. d^_^bIt is very interesting.I want to introduce <a href="http://www.efox-shop.com/"; rel="nofollow"> efox-shop</a> to you.
# Posted By ciphone | 7/30/10 11:19 PM
BlogCFC was created by Raymond Camden. This blog is running version 5.9.5.007. Contact Blog Owner