ANNOUNCEMENT: CFSQLTool Version 1.6p1 Release
This release of Switch_box CF SQLTool, V1.6p1, has many fixes and
enhancements. The main feature of
this release is the integration of table wrapper functions and CFC
instance variables. The reason for
the long post is to demonstrate the technical solution of the wrapper
approach. A wrapper CFC is
a set of CRUD functions that deliver and receive data between the
application and the database.
Typically, a wrapper is for one table or view. In the application,
the wrapper becomes an object
that encapsulates the wrapper functions and data structures. The attached
code demonstrates the
wrapper CFC and an application using the wrapper object.
The demonstration code uses a Contacts table with 4 columns, ContactID,
Name, Address and DateCreate.
There are demostration 2 files. Contacts.cfm is an application that uses
the Contacts wrapper to provide
the user with forms for listing, selecting, updating, and adding Contacts
information. The Contacts_wrap.cfc has
all the CRUD functions plus the basic data forms that Contacts.cfm uses
for data collection and display.
All the demonstration code was dynamically generated with the Switch_box
CF SQLTool by using
SQL Server meta-data schemas as the model for object names, variable
names and SQL function building.
By using the data model to drive the application data layers, the data
binding between the
two services is simplified allowing precise data transactions.
As you review the code you will notice variable names beginning with a $,
dollar sign.
Think of the $ as a data type much like using str in front of strName. In
this case,
the data type is of type CFC instance variable. The significance of
using $, is because
the CFC makes no distinction between different kinds of variables with
any declarative
built-in functions. As it turns out, the $ becomes very handy for program
code generation
and hand coding as well because it is an easy marker to follow through
the logic. For example,
if we create an object, objContact, that invokes the Contact_wrap.cfc,
references to
objContact.$Address means the instance variable named $Address in object
contact.
The integration of wrappers and instance variables with application logic
and database services is best
labeled as single point variable store using pass-by-reference access for
all functions.
This means that a variable value has to be stored only once and all
accesses to the variable
are by pointing to the value, not by passing the value. The approach CF
SQLTool uses to
implement this technique is that the namespace data storage location for
a data model variable is defined
when the wrapper for the data model is opened as an object. In
ColdFusion jargon,
each table has a wrapper CFC reflecting the table's data model where
table column names become
CFC instance variable names of the "this" scope. Because
CF can pass data structures to cffunction
as pass-by-reference, variable data can be stored when the value is
available and simply calling
the function without parameters is all that is necessary. This technique
is demonstrated in the code.
Look at the beginning of the CFC to see the instance variables name
spaces set. In each CRUD function is
an attribute named theParam with it's default value referencing the this
scope.
Further, any queryparam that requires data only has to reference a value
by stating the variable
name for the this scope name. Rarely does data have to be passed into the
CRUD function because the
query data is already stored in the CFC's scope.
More about this release can be found at:
http://www.switch-box.org/CFSQLTool/doc_1_6_ReleaseNotes.html
CF SQLTool is Open Source, GPL, no charge. The download is about 218K, so there is plenty of code. In the release notes
are some comments about a new wizard that I am considering for the tool -- object services.
Joseph
============== Comtacts.cfm ==================================
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<!--- Table CRUD Application for dbo_Contacts --->
<!--- {ts '2004-09-28 08:46:29'} --->
<head>
<title>objContacts</title>
</head>
<body>
<cfparam name="Request.IN.DSN" default="SwitchBoxSQLTool">
<cfparam name="Request.IN.SMV" default="New">
<!--- Invoke Table CRUD CFC --->
<cfinvoke component="Contacts_wrap" method="init" returnvariable="objContacts">
<cfset objContacts.SetInit(Request.IN.DSN) >
<!--- Instance variables
<cfscript>
// Instance variables for object
objContacts.$ContactsID = ""; //Null: No , Type: int, Char Len:, Num Pre: 10, Radix: 10, Scale: 0, Time Prec:
objContacts.$Name = ""; //Null: No , Type: varchar, Char Len:50, Num Pre: , Radix: , Scale: , Time Prec:
objContacts.$Address = ""; //Null: YES, Type: varchar, Char Len:50, Num Pre: , Radix: , Scale: , Time Prec:
objContacts.$DateCreate = ""; //Null: No , Type: datetime, Char Len:, Num Pre: 23, Radix: , Scale: 3, Time Prec: 3
</cfscript>
--->
<cfoutput>
<!--- Set request in to empty value --->
#objContacts.ActorIn()#
<!--- Use instance variables to name the request variable,
then set request variable values to cfc instance variables value --->
<cfset keylist = StructKeyList(objContacts)>
<cfloop index="key" list="#keylist#">
<cfif Mid(key, 1, 1) EQ "$" >
<cfset RequestKey = RemoveChars(key, 1, 1) >
<cfset objContacts[key] = Request.IN[RequestKey] >
<cfset Request.OUT[RequestKey] = Request.IN[RequestKey] >
</cfif >
</cfloop>
<h2><font color="Blue">objContacts</font> Wrapper Application</h2>
<cfswitch _expression_="#Request.IN.SMV#">
<cfcase value="New">
<h3>New Form </h3>
#objContacts.ActorOutIn()#
#objContacts.Form()#
#objContacts.ActorOut()#
<input type="submit" name="smv" id="smv" value="Insert">
<input type="submit" name="smv" id="smv" value="List">
#objContacts.EndForm()#
</cfcase>
<cfcase value="Insert">
<h3>Insert</h3>
<cfset objContacts.Create() >
#objContacts.Form()#
#objContacts.ActorOut()#
<input type="submit" name="smv" id="smv" value="Update">
<input type="submit" name="smv" id="smv" value="List">
#objContacts.EndForm()#
</cfcase>
<cfcase value="Select">
<h3>Select</h3>
<cfset objContacts.Read() >
#objContacts.Form()#
#objContacts.ActorOut()#
<input type="submit" name="smv" id="smv" value="Update">
<input type="submit" name="smv" id="smv" value="Delete">
<input type="submit" name="smv" id="smv" value="List">
<input type="submit" name="smv" id="smv" value="New">
#objContacts.EndForm()#
</cfcase>
<cfcase value="List">
<h3>List</h3>
#objContacts.Form()#
<cfset #objContacts.List()# >
<input type="submit" name="smv" id="smv" value="Select">
<input type="submit" name="smv" id="smv" value="Delete">
<input type="submit" name="smv" id="smv" value="New">
#objContacts.EndForm()#
</cfcase>
<cfcase value="Update">
<h3>Update</h3>
<cfset objContacts.Update() >
#objContacts.Form()#
#objContacts.ActorOut()#
<input type="submit" name="smv" id="smv" value="Update">
<input type="submit" name="smv" id="smv" value="List">
#objContacts.EndForm()#
</cfcase>
<cfcase value="Delete">
<h3>Deleted</h3>
<cfset objContacts.Delete() >
<cfset thisFile = GetFileFromPath(GetCurrentTemplatePath()) >
<cfset Request.IN.SMV = "List" >
<cfset getpagecontext().include(thisFile) >
</cfcase>
</cfswitch>
</cfoutput>
</body>
</html>
============== Contacts.cfm ==================================
============== Contacts_wrap.cfc ==============================
<cfcomponent
displayname="Contacts_wrap"
extends="com.switch_box.framework.udf"
hint="Comments: Demo contacts CFC; Title: CFC CRUD Wrapper; DSN: JosephTest; Table: dbo.Contacts; Version: {ts '2004-09-28 08:44:54'}; Iteration: 2">
<!--- Set instance variables --->
<cfscript>
this.$ContactsID = ""; //Null: No , Type: int, Char Len:, Num Pre: 10, Radix: 10, Scale: 0, Time Prec:
this.$Name = ""; //Null: No , Type: varchar, Char Len:50, Num Pre: , Radix: , Scale: , Time Prec:
this.$Address = ""; //Null: YES, Type: varchar, Char Len:50, Num Pre: , Radix: , Scale: , Time Prec:
this.$DateCreate = ""; //Null: No , Type: datetime, Char Len:, Num Pre: 23, Radix: , Scale: 3, Time Prec: 3
</cfscript>
<!--- ************************************************************ --->
<cffunction name="init" returntype="struct" hint="Init for the CFC" output="no">
<cfreturn this>
</cffunction>
<!--- ************************************************************ --->
<cffunction name="SetInit" returntype="boolean" hint="SetInit for the CFC" output="no">
<cfargument name="DSN" type="string" default="">
<cfset this.DSN = Arguments.DSN >
<cfreturn TRUE>
</cffunction>
<!--- ************************************************************ --->
<cffunction name="InstanceActorIn" returntype="void" output="no">
<cfscript>
this.$ContactsID = Request.IN.ContactsID;
this.$Name = Request.IN.Name;
this.$Address = Request.IN.Address;
this.$DateCreate = Request.IN.DateCreate;
</cfscript>
<cfreturn>
</cffunction>
<!--- ************************************************************ --->
<!--- ************************************************************ --->
<cffunction name="ActorIn" returntype="void" output="yes">
<cfparam name="Request.IN.ContactsID" default="">
<cfparam name="Request.IN.Name" default="">
<cfparam name="Request.IN.Address" default="">
<cfparam name="Request.IN.DateCreate" default="">
<cfreturn>
</cffunction>
<!--- ************************************************************ --->
<!--- ************************************************************ --->
<cffunction name="ActorOutIn" returntype="void" output="yes">
<cfparam name="Request.OUT.ContactsID" default="#Request.IN.ContactsID#">
<cfparam name="Request.OUT.Name" default="#Request.IN.Name#">
<cfparam name="Request.OUT.Address" default="#Request.IN.Address#">
<cfparam name="Request.OUT.DateCreate" default="#Request.IN.DateCreate#">
<cfreturn>
</cffunction>
<!--- ************************************************************ --->
<!--- ************************************************************ --->
<cffunction name="Create" returntype="boolean">
<cfargument name="theParams" type="struct" default="#this#" required="yes" >
<cfargument name="DSN" type="string" default="#this.DSN#">
<cfset var qContacts_INS = "" >
<cfquery name="qContacts_INS" datasource="#Arguments.DSN#" maxrows=-1 >
BEGIN TRANSACTION
INSERT INTO dbo.Contacts
(
[Name],
[DateCreate]
)
VALUES
(
<cfqueryparam value="#theParams.$Name#" cfsqltype="CF_SQL_varchar" >,
<cfqueryparam value="#theParams.$DateCreate#" cfsqltype="CF_SQL_DATE" >
)
COMMIT TRAN
</cfquery>
<cfreturn TRUE>
</cffunction>
<!--- ************************************************************ --->
<!--- ************************************************************ --->
<cffunction name="Update" returntype="boolean">
<cfargument name="theParams" type="struct" default="#this#" required="yes" >
<cfargument name="DSN" type="string" default="#this.DSN#">
<cfset var qGet_Contacts_UPD = "" >
<cfquery name="qGet_Contacts_UPD" datasource="#Arguments.DSN#" maxrows=-1 >
BEGIN TRANSACTION
UPDATE dbo.Contacts
SET
[Name] = <cfqueryparam value="#theParams.$Name#" cfsqltype="CF_SQL_varchar" >,
[Address] = <cfqueryparam value="#theParams.$Address#" cfsqltype="CF_SQL_varchar" NULL="#isNull(theParams.$Address)#" >,
[DateCreate] = <cfqueryparam value="#theParams.$DateCreate#" cfsqltype="CF_SQL_DATE" >
WHERE
(
ContactsID = <cfqueryparam value="#theParams.$ContactsID#" cfsqltype="CF_SQL_INTEGER" >
)
COMMIT TRAN
</cfquery>
<cfreturn TRUE >
</cffunction>
<!--- ************************************************************ --->
<!--- ************************************************************ --->
<cffunction name="Delete" returntype="boolean" >
<cfargument name="theParams" type="struct" default="#this#" required="yes" >
<cfargument name="DSN" type="string" default="#this.DSN#">
<cfset var qGet_Contacts_DEL = "" >
<cfquery name="qGet_Contacts_DEL" datasource="#Arguments.DSN#" >
BEGIN TRANSACTION
DELETE
FROM dbo.Contacts
WHERE ContactsID = <cfqueryparam value="#theParams.$ContactsID#" cfsqltype="CF_SQL_INTEGER" >
COMMIT TRAN
</cfquery>
<cfreturn TRUE>
</cffunction>
<!--- ************************************************************ --->
<!--- ************************************************************ --->
<cffunction name="List" returntype="void" output="yes" >
<cfargument name="MaxRow" type="string" default="-1" >
<cfargument name="DSN" type="string" default="#this.DSN#">
<cfset var qGet_Contacts_LST = "" >
<cfquery name="qGet_Contacts_LST" datasource="#Arguments.DSN#" maxrows="#Arguments.MaxRow#" >
SELECT [Name],[Address],[DateCreate]
FROM dbo.Contacts
</cfquery>
<cftable query="qGet_Contacts_LST" maxrows="#Arguments.MaxRow#" colheaders htmltable>
<cfcol header="Name" text="<input type='radio' name='ContactsID' value='#qGet_Contacts_LST.ContactsID#'> #Name#" align="LEFT">
<cfcol header="Address" text="#Address#" align="LEFT">
<cfcol header="DateCreate" text="#DateCreate#" align="LEFT">
</cftable>
<cfreturn>
</cffunction>
<!--- ************************************************************ --->
<!--- ************************************************************ --->
<!--- This function is an open-ended Select statement that has no constraint WHERE clause.
Passing in the WHERE clause as a parameter constrains the Select. --->
<cffunction name="ReadAll" returntype="query">
<cfargument name="where" type="string" default="WHERE 0=0">
<cfargument name="DSN" type="string" default="#this.DSN#">
<cfset var qGet_Contacts_SELECT = "" >
<cfquery name="qGet_Contacts_SELECT" datasource="#Arguments.DSN#" maxrows=-1>
SELECT TOP 100 PERCENT ContactsID,Name,Address,DateCreate
FROM dbo.Contacts
#PreserveSingleQuotes(Arguments.Where)#
</cfquery>
<cfif qGet_Contacts_SELECT.recordcount eq 1 >
<cfset Request.OUT.ContactsID = qGet_Contacts_SELECT.ContactsID >
<cfset Request.OUT.Name = qGet_Contacts_SELECT.Name >
<cfset Request.OUT.Address = qGet_Contacts_SELECT.Address >
<cfset Request.OUT.DateCreate = qGet_Contacts_SELECT.DateCreate >
</cfif>
<cfreturn qGet_Contacts_SELECT >
</cffunction>
<!--- ************************************************************ --->
<!--- ************************************************************ --->
<cffunction name="ReadByContactID" returntype="query">
<cfargument name="theParams" type="struct" default="#this#" required="yes" >
<cfargument name="DSN" type="string" default="#this.DSN#">
<cfset var qGet_Contacts_SEL = "" >
<cfquery name="qGet_Contacts_SEL" datasource="#Arguments.DSN#" maxrows="-1">
SELECT TOP 100 PERCENT [ContactsID],[Name],[Address],[DateCreate]
FROM dbo.Contacts
WHERE ( [ContactsID] = <cfqueryparam value="#theParams.$ContactsID#" cfsqltype="CF_SQL_INTEGER" >)
</cfquery>
<cfif qGet_Contacts_SEL.recordcount eq 1 >
<cfset Request.OUT.ContactsID = qGet_Contacts_SEL.ContactsID >
<cfset Request.OUT.Name = qGet_Contacts_SEL.Name >
<cfset Request.OUT.Address = qGet_Contacts_SEL.Address >
<cfset Request.OUT.DateCreate = qGet_Contacts_SEL.DateCreate >
</cfif>
<cfreturn qGet_Contacts_SEL >
</cffunction>
<!--- ************************************************************ --->
<!--- ************************************************************ --->
<cffunction name="ActorOut" returntype="void" output="yes">
<input type="hidden" name="bot_table" value="Contacts">
<cfoutput>
<input type="hidden" name="ContactsID" value="#Request.OUT.ContactsID#">
<input type="hidden" name="bot_formfield" value="ContactsID">
<input type="hidden" name="bot_formfield" value="ContactsID,Name,Address,DateCreate">
<table>
<tr>
<td align="right">
ContactsID
</td>
<td align="left">
<input type="text" name="ContactsID" value="#Request.OUT.ContactsID#">
</td>
</tr> <tr>
<td align="right">
Name
</td>
<td align="left">
<input type="text" name="Name" value="#Request.OUT.Name#">
</td>
</tr> <tr>
<td align="right">
Address
</td>
<td align="left">
<input type="text" name="Address" value="#Request.OUT.Address#">
</td>
</tr> <tr>
<td align="right">
DateCreate
</td>
<td align="left">
<input type="text" name="DateCreate" value="#Request.OUT.DateCreate#">
</td>
</tr>
</table>
</cfoutput>
<cfreturn>
</cffunction>
<!--- ************************************************************ --->
<!--- ************************************************************ --->
<cffunction name="form" returntype="string" output="yes">
<cfargument name="action" type="string" default="#cgi.script_name#">
<cfargument name="method" type="string" default="post">
<cfargument name="target" type="string" default="_self">
<cfset var strReturn = "<form action='' method='#Arguments.Method#' target='#Arguments.target#' >" >
<cfreturn strReturn >
</cffunction>
<!--- ************************************************************ --->
<!--- ************************************************************ --->
<cffunction name="endform" returntype="string" output="yes">
<cfreturn "</form>" >
</cffunction>
<!--- ************************************************************ --->
</cfcomponent>
============== Contacts_wrap.cfc ==============================
