Archive for the ‘Coldfusion 9’ Category

Setting print margins using Eclipse to maintain line character length

Friday, May 14th, 2010

Sometimes you may have standard line lengths with which to work when writing code. If this is the case and your using Coldfusion and Eclipse with the cfEclipse plugin you can maintain the character length consistency by setting up the print margins to n character length.

Point being that when running the cfeclipse plugin you need to choose CFEclipse > Editor in the preferences left navigation. In the options you will check ‘Show print margin’, set the ‘Print margin column’ text field to the desired character length, and if you choose the ‘Print margin’ option in the ‘Appearance color options’ select box. Then choose the color you wish the margin to be. This will set a line length that will show in the code editor pane. Of course it will not wrap the line. It is still your duty as a developer to wrap appropriately. Nonetheless it does give you a guide for where the line should wrap at least.

If you’re writing Java then you would select General > Editor > Text Editors in the preferences pane.  Although the display is slightly different, generally follow the same steps as above for showing the print margin.

If you’re using Coldfusion Builder you will follow the same exact directions as listed just above for showing the print margins writing Java.

Lastly, if you’re using Flex Builder 3 you will again follow the same directions as above for showing print margins writing Java.

Connecting to the MailChimp API with Coldfusion

Sunday, April 25th, 2010

I have just started playing with the MailChimp API with Coldfusion. As usual, there’s not a lot of Coldfusion documentation or examples. So I thought I’d start posting some of the how-to’s as I myself figure them out. Here will demonstrate how to make a simple connection with the API so you can start doing more productive things like adding emails to lists and getting stats.

Let’s begin at the beginning of using any API. We need an account and an API key. Go ahead and create an account as if you’re a client, or if you are doing this for an actual client go ahead and create an account for them. MailChimp makes this super simple. Once you have an account click on the ‘account’ button in the top left corner of the dashboard. This will take you to a set of links. Click the ‘API Keys & Info’ link. This will take you to a page where you can manage your API keys. Clicking ‘Add A Key’ will generate a new key for you to use.

Now that we have our key and account we’re ready to make our connection via Coldfusion. Before looking at any code, let’s make sure we know where our documentation is at. You can find the MailChimp docs here. Requesting data from MailChimp requires you to pass the method and parameters via the url: http://us1.api.mailchimp.com/1.2/. As parameters you need to indicate the type of output MailChimp should return data as. In our case we’ll use JSON. So we’ll attach ‘?output=json’. Next we need to specify the method. MailChimp has a ping() method meant for no other reason than to make sure the connection is working. So this is what we’ll use here. To do this we attach the method parameter ‘&method=ping’. If you look at the docs you’ll see that this requires only one parameter to work, which is the API key (this is required for all methods). So we’ll attach another parameter ‘&apikey=[yourapikey]‘.

So let’s look at how to write this using Coldfusion.

<!--- SET URL --->
<cfset VARIABLES.api_url = "http://us1.api.mailchimp.com/1.2/?output=json&method=ping&apikey=[your key]-us1">

<!--- SEND DATA VIA CFHTTP TAG --->
<cfhttp name="call" url="#VARIABLES.api_url#" result="VARIABLES.mycall">

<!--- BECAUSE RETURNED AS JSON WE NEED TO DESERIALIZE THE RESPONSE --->
<cfset VARIABLES.deserialized = deserializeJSON(VARIABLES.mycall.fileContent)>

<!--- DUMP RESPONSE --->
<cfdump var="#VARIABLES.deserialized#">

First we set our URL. In this case you’ll, of course, change the ‘[your key]‘ to whatever your actual key is that MailChimp generated for you in your account. Next we use the Coldfusion tag to send the url to MailChimp. MailChimp then processes it and sends back the result which we store as ‘VARIABLES.mycall’. Because we specified to MailChimp to return data back to us as JSON we need to make sure we deserialize it before displaying it. We do this using the ‘deserializeJSON()’ method. Also the response is passed back as a struct with the actual data from MailChimp stored in the ‘fileContent’ key. Lastly we just dump the data so we can see that ‘Everything’s Chimpy!’.

At this point you should be able to start playing more with the API to do more complex and practical things.

Using Coldfusion to specify which pages to run under an SSL

Sunday, April 25th, 2010

I recently had a site that ran under an SSL certificate. As this was the case each page of the site ran under the ‘https://’ protocol. A few weeks ago the client noticed that when run in Internet Explorer they were receiving a message stating that not all of the data was being passed via the SSL. It would ask if the user wished to load in the insecure data as well as the secure. If the user chose yes everything loaded as normal. If they chose no everything except AJAX data would load onto the page.

While this makes sense I’m not sure why it just started happening. Nonetheless, I noted that it was not necessary to run the entire site under the SSL. Since I, of course, do not use AJAX anywhere in the checkout process, I decided that the simple solution was to run the SSL only under the checkout. This would allow the rest of the site to run under the ‘http://’ protocol without any security prompts concerning AJAX from Internet Explorer. So let’s take a look at how to run the SSL under only select pages in your site.

<!--- ***************  MAKE SURE THE SSL IS IN PLACE  *************** --->
<cfset var storepath = "http://www.yourdomain.com/checkout/">
<cfset var url = "http://" & CGI.HTTP_HOST & CGI.SCRIPT_NAME>
<cfset var isinstore = findNoCase(storepath, url)>

<!--- IF FOUND CHANGE TO SECURE PROTOCOL --->
<cfif isinstore neq 0 AND CGI.SERVER_PORT neq "443">
	<!--- SET TO SECURE PROTOCOL --->
	<cfset url = "https://" & CGI.HTTP_HOST & CGI.SCRIPT_NAME>

	<!--- SET QUERY STRING IF NECESSARY --->
	<cfif CGI.QUERY_STRING neq "">
		<cfset url = url & "?" & CGI.QUERY_STRING>
	</cfif>

	<!--- REDIRECT USER --->
	<cflocation url="#url#" addtoken="false">
</cfif>

This snippet of code is run under the onRequestStart() method in the Application.cfc. This allows the program to check the url with each request that is made. Some pre-planning is required to choose where the SSL is run without just doing a page by page check. In this case we have made sure that we have placed all of our secure pages under a single directory. In this case we are wanting to secure our ‘checkout’ directory. If we are under this directory we also want to make sure that we are not running on the secure port 443. If not we can take the current url, run the secure protocol at the beginning, attach the query string if necessary, and then redirect the user to the secure page.

With each request Coldfusion checks if the page falls under the ‘checkout’ directory and if so it redirects to itself using the ‘https://’ protocol. For this protocol to work, it of course assumes that you have set up the SSL in IIS or whatever you use. I also recommend going into the site and changing any absolute paths linking to any of the pages running under your secure directory to use the secure protocol by default.

Looping over lists

Wednesday, April 21st, 2010

I realize that the title implies a super basic topic, and it certainly does. But this is something that tripped me up a little earlier today so I thought that simple as it may be I would share for anyone else encountering a similar problem. My issue was that I created a list, looped over the list, for each loop checked the value, and depending on the value output a new value. What I found was that my switch statements would pick up the first value in the list but not the rest. The problem ended up being that I was putting a space between my comma and the next value. Let’s look at a quick example of this.

<!--- SET A LIST --->
<cfset VARIABLES.list = "RI, MA, CT">

<!--- LOOP OVER LIST --->
<cfloop list="#VARIABLES.list#" delimiters="," index="VARIABLES.i">
	<!--- CHECK ITEM IN LIST AND SHOW FULL STATE NAME --->
	<cfswitch expression="#VARIABLES.i#">
		<cfcase value="RI">
			<cfset VARIABLES.state = "Rhode Island">
		</cfcase>

		<cfcase value="MA">
			<cfset VARIABLES.state = "Massachusetts">
		</cfcase>

		<cfcase value="CT">
			<cfset VARIABLES.state = "Connecticut">
		</cfcase>
	</cfswitch>

	<!--- DISPLAY FULL STATE NAME --->
	<cfoutput>
		#VARIABLES.state#<br />
	</cfoutput>
</cfloop>

This will display “Rhode Island” three times. What causes this? Again it’s that space after the commas in my initial list. As it stands the the case value is looking for ” MA” not “MA”.

If you convert the items in the list into a byte array you will see that both MA and CT have an extra character which is a space. Simple and obvious solution though. Just make sure that your lists are nice and tight. While I have a tendency to use arrays as opposed to lists in these cases I really find it hard to believe that I’m just now noticing this.

VARIABLES scope in CFCs in Coldfusion 9

Sunday, April 11th, 2010

The point is to hopefully add a little clarification to the VARIABLES scope used inside of CFCs in Coldfusion 9.  I’ll also be using a few other things like an init() method to further illustrate the use of the VARIABLES scope.  I’ll be using simple examples of these things for demonstration purposes only.  That being said, we’ll look at the first example which will demonstrate the ‘VARIABLES’ scope in a cfc as well as using an ‘init()’ method to set up a CFC.  Let’s take a look at the component.

<cfcomponent displayname="Manage Person" hint="Manages the persons created." output="false">

	<!--- **************************  INITIALIZE  ************************** --->
	<cffunction name="init" displayname="Initialize" description="Sets the variables that will need to be used throughout this component." output="false" access="public" returntype="any">

		<cfset VARIABLES.name = "Matthew">
		<cfset VARIABLES.haircolor = "Brown">
		<cfset VARIABLES.eyecolor = "Hazel">
		<cfset VARIABLES.height = "5 feet, 6 inches">
		<cfset VARIABLES.weight = "130 lbs">
		<cfset VARIABLES.gender = "Male">

		<cfreturn THIS>

	</cffunction>

	<!--- **************************  ADD PERSON  ************************** --->
	<cffunction name="addPerson" displayname="Add Person" description="Creates a person object." access="public" output="false" returntype="struct">

		<!--- BUILD STRUCT --->
		<cfset var data = structNew()>
		<cfset data.success = true>
		<cfset data.message = "">
		<cfset data.person = structNew()>
		<cfset data.person.name = VARIABLES.name>
		<cfset data.person.haircolor = VARIABLES.haircolor>
		<cfset data.person.eyecolor = VARIABLES.eyecolor>
		<cfset data.person.height = VARIABLES.height>
		<cfset data.person.weight = VARIABLES.weight>
		<cfset data.person.gender = VARIABLES.gender>

		<!--- RETURN STRUCT --->
		<cfreturn data />

	</cffunction>

	<!--- *****************************  SET NAME  ***************************** --->
	<cffunction name="setName" displayname="Set Name" description="Sets a persons name into the component." access="public" output="false" returntype="void">

		<!--- ARGUMENTS --->
		<cfargument name="name" displayname="Person Name" hint="The name of a person." type="string" required="true" />

		<!--- SET NAME INTO THIS SCOPE --->
		<cfset VARIABLES.name = ARGUMENTS.name>

	</cffunction>

	<!--- **************************  SET EYE COLOR  ************************** --->
	<cffunction name="setEyeColor" displayname="Set Eye Color" description="Sets the eye color of a person." access="public" output="false" returntype="void">

		<!--- ARGUMENTS --->
		<cfargument name="eyecolor" displayname="Person Eye Color" hint="The eye color of a person." type="string" required="true" />

		<!--- SET EYE COLOR INTO THIS SCOPE --->
		<cfset VARIABLES.eyecolor = ARGUMENTS.eyecolor>

	</cffunction>

	<!--- **************************  SET HAIR COLOR  ************************** --->
	<cffunction name="setHairColor" displayname="Set Hair Color" description="Sets the hair color of a person." access="public" output="false" returntype="void">

		<!--- ARGUMENTS --->
		<cfargument name="haircolor" displayname="Person Hair Color" hint="The hair color of a person." type="string" required="true" />

		<!--- SET HAIR COLOR INTO THIS SCOPE --->
		<cfset VARIABLES.haircolor = ARGUMENTS.haircolor>

	</cffunction>

	<!--- **************************  SET WEIGHT  ************************** --->
	<cffunction name="setWeight" displayname="Set Weight" description="Sets the weight of a person" access="public" output="false" returntype="void">

		<!--- ARGUMENTS --->
		<cfargument name="weight" displayname="Person Weight" hint="The weight of a person." type="string" required="true" />

		<!--- SET WEIGHT INTO THIS SCOPE --->
		<cfset VARIABLES.weight = ARGUMENTS.weight>

	</cffunction>

	<!--- **************************  SET GENDER  ************************** --->
	<cffunction name="setGender" displayname="Set Gender" description="Sets the gender of a person" access="public" output="false" returntype="void">

		<!--- ARGUMENTS --->
		<cfargument name="gender" displayname="Person Gender" hint="The gender of a person." type="string" required="true" />

		<!--- SET GENDER INTO THIS SCOPE --->
		<cfset VARIABLES.gender = ARGUMENTS.gender>

	</cffunction>

	<!--- **************************  SET HEIGHT  ************************** --->
	<cffunction name="setHeight" displayname="Set Height" description="Sets the height of a person" access="public" output="false" returntype="void">

		<!--- ARGUMENTS --->
		<cfargument name="height" displayname="Person Height" hint="The height of a person." type="string" required="true" />

		<!--- SET HEIGHT INTO THIS SCOPE --->
		<cfset VARIABLES.height = ARGUMENTS.height>

	</cffunction>

</cfcomponent>

We have a simple cfc here with an init() method, an addPerson() method, and several setter methods for setting different attributes of a person. The scope being used here is the VARIABLES scope. This scope persists with the instance of the component. In other words, until the a new instance is created any data stored in the VARIABLES scope will be available to all functions inside the component.

The init() method is called when we create an instance of the component. Upon creation we simply set a few pieces of data into the VARIABLES scope preparing the component to use any of this data in any of the following methods. In this case I go ahead and simply create a default person object, but could easily leave this data blank and rely on the set methods to fill in the person object attributes.

The next method is the addPerson() method which just compiles all of the attributes that make a person object and store it into a struct called ‘data’. Notice though that none of the values of a person are set in this method. It’s only job is to lump it all into a struct. We can do this because our data is stored in the VARIABLES scope.

The remaining functions just set data into the VARIABLES scope. Notice that they simply accept a value as an ARGUMENT, and then take that value and set it into the VARIABLES scope. This essentially overrides the value set initially in the init() method. Let’s look now at how this would be called from the .cfm page.

(more…)

Getting the full file path of a Coldfusion Component in dot-notation

Sunday, April 11th, 2010

When you are creating an instance of a cfc using the <cfobject> tag or createObject() method you need to specify the path to the desired component via a dot-notation file path.  All-in-all this is little more than a file path where the back-slashes are replaced with periods.  However, these paths are not full local paths like when using an absolute path for an image for example.  So the file path will not begin with ‘/Applications’ (if on a mac) or ‘C:/’ (if on windows).  Instead the path begins in the server root, which for Coldfusion is the ‘wwwroot’ directory.

If you are running a package of cfcs or are creating instances from different parts of your application I would recommend creating a mapping to the package or directory containing the cfc(s).  This will save you some work in the future if, say, the application directory structure changes.  You can get the full dot-notation path by introspecting the cfc you are creating an instance of.  I’ve created a short video showing a few ways of getting this file path pretty quickly.  Watch the video.

Stripping out HTML tags from text with Coldfusion 9

Wednesday, April 7th, 2010

I’ve built a fairly simple UDF for stripping out HTML from a chunk of text. I’ll quickly explain my logic and what this UDF can do.

First, there are two types of tags in HTML. One is a wrapping tag. An example of this is the bold tag.

<b>Some content</b>

Next is a self-contained tag.

<img src="my/path/image.jpg" />

If the tag is a wrapping tag we need to make the decision of whether or not to delete the content the tag is wrapping. For example the content inside a bold tag we will most likely want to keep. While the content inside a script tag we will want to delete.

When it comes right down to it, we need to make three decisions about the HTML being deleted.

  1. What tag needs to be deleted.
  2. Is this a wrapping tag.
  3. If a wrapping tag, should the content be deleted.

So let’s take a look at the UDF.

<cfcomponent displayname="Strip HTML" hint="Strips out of a set of given text any specified html tags." output="false">

	<!--- ************************  STRIP HTML  ************************ --->
	<cffunction name="stripHtml" displayname="Strip HTML" description="Strips out specified HTML tags." access="public" output="false" returntype="struct">

		<!--- ARGUMENTS --->
		<cfargument name="text" displayName="Text" type="string" hint="Text to strip out html tags from." required="true" />
		<cfargument name="tags" displayName="Tags" type="string" hint="Tags to be striped from the text.  Ex. '[string:tag name],[what to remove - {string:tag | string:content}],[is it a wrapping tag? {boolean}]'. Tags are delimited with semi-colons." required="true" />

		<!--- SET SOME LOCAL VARS --->
		<cfset var textbytes = "">
		<cfset var counter = 1>
		<cfset var delete = false>
		<cfset var temp = "">
		<cfset var tagtoberemoved = "">
		<cfset var whatgetsremoved = "">
		<cfset var wrappingtag = "">

		<!--- BUILD STRUCT --->
		<cfset var data = structNew()>
		<cfset data.success = true>
		<cfset data.message = "">
		<cfset data.orginaltext = ARGUMENTS.text>
		<cfset data.strippedtext = ARGUMENTS.text>

		<!--- CHECK IF ALL CONTENT SHOULD BE REMOVED --->
		<cfif ARGUMENTS.tags eq "all">
			<!--- REMOVE HTML TAGS --->
			<cfset data.strippedtext = rereplaceNoCase(ARGUMENTS.text, "<[^>]*>", "", "all")>
		<cfelse>
			<!--- LOOP OVER THE LIST OF TAGS TO BE REMOVED --->
			<cfloop list="#ARGUMENTS.tags#" index="VARIABLES.i" delimiters=";">
				<!--- SET ATTRIBUTES OF TAG TO BE DELETED --->
				<cfset tagtoberemoved = listFirst(VARIABLES.i, ",")>
				<cfset whatgetsremoved = listGetAt(VARIABLES.i, 2, ",")>
				<cfset wrappingtag = listLast(VARIABLES.i, ",")>

				<!--- IF REMOVING JUST THE TAG --->
				<cfif whatgetsremoved eq "tag">
					<!--- CHECK IF IT IS A WRAPPING TAG --->
					<cfif wrappingtag eq true>
						<!--- REMOVE WRAPPING TAG, BUT NOT THE CONTENT --->
						<cfset data.strippedtext = rereplaceNoCase(data.strippedtext, "<#tagtoberemoved#>", "", "all")>
						<cfset data.strippedtext = rereplaceNoCase(data.strippedtext, "</#tagtoberemoved#>", "", "all")>
					<cfelse>
						<!--- REMOVE CONTAINED TAG --->
						<cfset data.strippedtext = rereplaceNoCase(data.strippedtext, "<#tagtoberemoved# />", "", "all")>
					</cfif>

				<!--- IF REMOVING TAG AND CONTENT --->
				<cfelseif whatgetsremoved eq "content">
					<!--- CHECK IF IT IS A WRAPPING TAG --->
					<cfif wrappingtag eq true>
						<!--- REMOVE THE TAG AND CONTENT --->
						<cfset data.strippedtext = rereplaceNoCase(data.strippedtext, "<#tagtoberemoved#>.*</#tagtoberemoved#>", "", "all")>
					<cfelse>
						<!--- REMOVE CONTAINED TAG --->
						<cfset data.strippedtext = rereplaceNoCase(data.strippedtext, "<#tagtoberemoved# />", "", "all")>
					</cfif>
				</cfif>
			</cfloop>
		</cfif>

		<!--- RETURN STRUCT --->
		<cfreturn data>

	</cffunction>

</cfcomponent>

The method takes two arguments. First is the chunk of text to have HTML stripped out of. Second is information about the HTML to strip out of the text. The latter argument accepts a string. Multiple tags can be specified by separating them with semi-colons. For each tag that needs deleted three pieces of information needs to be provided. Each of the three pieces are delimited with a comma. These are the three arguments listed above. First is the name of the tag. So if we are wanting to remove an img tag we would specify “img”, for bold “b”. Second, we specify if we want just the tag removed or the tag and the content it wraps. There are two acceptable strings here – “tag” (for just removing the tag) and “content” (for removing the tag and the content). Lastly, specify whether this is a wrapping or contained tag. For an image tag we would specify “false” because it is not a wrapping tag. For bold we would specify “true” because it is a wrapping tag.

One last note, if you want to strip out all HTML tags you can just pass in the string “all” to the method. But note that this will just strip out the HTML and any content being wrapped by the tags will be left alone.

So let’s look at an example of how to call this tag.

<!--- TEXT TO STRIP HTML FROM --->
<cfset VARIABLES.text = "Lorem <img />ipsum <b>dolor</b> sit amet, <em>consectetur</em> adipiscing elit.>

<!--- STRIP OUT ALL HTML --->
<cfset stripHtml(VARIABLES.text, "all")>

<!--- STRIP OUT IMG, B, AND EM TAGS --->
<cfset stripHtml(VARIABLES.text, "img,tag,false;b,tag,true;em,content,true")>

Feel free to copy this and use it as you please.

Directory Watcher Gateway for file management logging

Sunday, April 4th, 2010

I wanted to play with gateways a little bit in Coldfusion 9 this weekend, but wanted to build something at least slightly practical.  What I ended up with is  a gateway that keeps up with file additions, changes, and deletions for a given project and logs those changes to a file.  This was surprisingly easy to accomplish with the directory-watcher gateway that ships with Coldfusion.  All in all this is a tool I may implement for development purposes just to have a running log of file changes that occur within a given project over the given amount of time.

So let’s get this ball rolling by creating a couple projects under the ‘wwwroot’ folder in our Coldfusion9 directory.  If you are still using Coldfusion 8 I don’t see any reason why this won’t work for you as well.  I haven’t tested it though.  For me, I named the first project ‘my_site’ and the second ‘my_othersite’.  Now set up two projects in Coldfusion Builder that respectively points to these two directories.

Now we’ll create a set of directories in each of these projects.  Under the project you should create a directory called ‘gateways’.  Under that directory create another directory called ‘cfcs’ and another one called ‘config’.  Note that this is how that I’ve chosen to do this.  You can set up your file structure however you want.  The main thing to note is that you will need the configuration file for the directory-watcher gateway in your project.  This way when you create your instance of the gateway in the Coldfusion Administrator you can point it to this specific project.  You will then be able to set a new configuration file in another project (which we’ll do) allowing you to watch multiple directories at once.  Inside of the ‘cfcs’ directory you can create a component called ‘directory_watcher.cfc’, and under the ‘config’ directory create a file called ‘directory-watcher.cfg’.  So your file structure should look like the following.

File Structure

File Structure

Now we’ll set up the directory-watcher.cfg file.  Under your ‘ColdFusion9′ directory navigate to ‘gateway/config’ and open the ‘directory-watcher.cfg’ file.  Copy the contents to your file in the my_site project.  Note: you could also just copy the entire file to your ‘config’ directory if you like.  The contents will look like the following.

directory-watcher.cfg

#
# DirectoryWatcherGateway configuration file
#

# The directory you want to watch.  If you are entering a Windows path
# either use forward slashes (C:/mydir) or escape the back slashes (C:\\mydir).
directory=

# Should we watch the directory and all subdirectories too
# Default is no.  Set to 'yes' to do the recursion.
recurse=no

# The interval between checks, in miliseconds
# Default is 60 seconds
interval=60000

# The comma separated list of extensions to match.
# Default is * - all files
extensions=*

# CFC Function for file Change events
# Default is onChange, set to nothing if you don't want to see these events
changeFunction=onChange

# CFC Function for file Add events
# Default is onAdd, set to nothing if you don't want to see these events
addFunction=onAdd

# CFC Function for file Delete events
# Default is onDelete, set to nothing if you don't want to see these events
deleteFunction=onDelete

We’re going to make just a couple changes to this file.  First and foremost we need to add the directory for the gateway to watch.  This is the my_site directory we created under the ‘wwwroot’ directory.  I’m on a mac so I would write ‘/Applications/ColdFusion9/wwwroot/my_site’ after the ‘directory=’ at the beginning of the file.  Next we need to set ‘recurse=no’ to ‘recurse=yes’.  This will allow our gateway to watch this directory and all directories beneath it.  Lastly, just so we can see our changes more quickly we can set the interval to ‘10000′ milliseconds.  By default it runs every minute.  So our new file will look like:

(more…)

Deleting from multiple SQL tables with a single query

Wednesday, March 31st, 2010

I’m by no means an expert using SQL, but as of yet I’ve found no way to delete data from multiple tables without writing a single query specifically for each table that data needs deleted from. So if you’re reading this expecting a native SQL statement that will delete data from multiple tables then I have nothing to offer. However, I do have a solution. One which requires a programming language, Coldfusion in my case.

The idea is that we have a number of tables.  Lets say we have five tables holding data about products.  So we’ll have tables named ‘products_tbl’, ‘products_name_tbl’, ‘products_description_tbl’.  Using just SQL we would need to write three delete statements like:

Straight SQL

<cfquery name="VARIABLES.deletetable" datasource="myds">
	DELETE
	FROM product_tbl
	WHERE product_id = 9
</cfquery>

<cfquery name="VARIABLES.deletetable" datasource="myds">
	DELETE
	FROM product_name_tbl
	WHERE product_id = 9
</cfquery>

<cfquery name="VARIABLES.deletetable" datasource="myds">
	DELETE
	FROM product_description_tbl
	WHERE product_id = 9
</cfquery>

Now let’s say that halfway through the project the client suddenly wants the price to be included. Really not a big deal, but when we now go to delete the product we realize that we need to go find our component or whatever that is handling the deleting transactions and add a new delete statement.

<cfquery name="VARIABLES.deletetable" datasource="myds">
	DELETE
	FROM product_price_tbl
	WHERE product_id = 9
</cfquery>

I personally hate this, and like most things the more complicated the data becomes the more burdensome it gets on the developer. So what’s the solution?

The idea is that we need to bundle all of these related sets of data into a group. When I say related I simply mean that in this case all the tables containing the parts and pieces of a product. The product is an object, and when that object is deleted we need to visit each of the tables containing information for that product and get rid of it. If we could simply group each of the table names we could run a loop with a delete statement inside where the table name dynamically changes with each loop. This would allow us to update our group and not have to worry about updating any actual SQL.

In my case, I’ve created the group under the APPLICATION scope. This let’s me access the group from anywhere within my application. As such my Application file would contain inside the onApplicationStart() method, for this case, the following.

<cfparam name="APPLICATION.producttables.productTbl" type="string" default="product_tbl">
<cfparam name="APPLICATION.producttables.productNameTbl" type="string" default="product_name_tbl">
<cfparam name="APPLICATION.producttables.productDescriptionTbl" type="string" default="product_description_tbl">

Now that we have our table names stored in the APPLICATION scope under a group called ‘producttables’ we can build a function that will get all the data in the ‘producttables’ group and set it into an array. We can then loop over this array of table names deleting from each the product data associated with the specified product id. In this case our function would look like.

<cfcomponent displayname="Looping Delete" hint="Loops over a portion of a struct with table names and deletes from those tables." output="false">

	<cffunction name="deleteProduct" displayname="Delete Product" description="Deletes a product." access="public" output="false" returntype="void">

		<!--- PRODUCT ID --->
		<cfargument name="productid" displayname="Product ID" hint="Id of the product to be deleted." type="numeric" required="true" />

		<!--- CREATE AN ARRAY OF TABLE NAMES STORED IN APPLICATION SCOPE --->
		<cfset var tableArray = structkeyarray(APPLICATION.producttables)>

		<!--- LOOP OVER THE ARRAY --->
		<cfloop array="#tblArray#" index="VARIABLES.i">
			<cfquery name="deleteproduct" datasource="#APPLICATION.dataSource#">
				DELETE
				FROM schema_name.#APPLICATION.producttables[VARIABLES.i]#
				WHERE product_id = <cfqueryparam value="#ARGUMENTS.productid#" cfsqltype="cf_sql_integer" maxlength="10">
			</cfquery>
		</cfloop>

	</cffunction>

</cfcomponent>

So what we’ve done here is made it much easier to add data to the product without ever having to touch our component. Instead all we really need to do is add the data to the APPLICATION scope to the producttables group. So for instance now if we need to add a price to our product all we have to do is go into the Application.cfc file and set

<cfparam name="APPLICATION.producttables.productPriceTbl" type="string" default="product_price_tbl">

That’s all that we need now to make the update. The loop will now find this table in our group and delete from it the same as the others.

Cflayout first tab does not show content bug

Monday, March 29th, 2010

Another issue I ran into when converting some older Coldfusion 8 applications over to a Coldfusion 9 server concerning tabs using the tag. Unlike getDataSource() this issue is a Coldfusion 9 bug. The issue is that if you create a tab layout the first tab does not have an auto-height property. That being said, no matter how much content you put inside the region it will never expand to show the content.

MrTee posted on the Coldfusion 9 docs both the problem and the solution. This is not my solution, I’m just spreading the word here. The link to his comment is here. He has also posted the correct javascript file here.

The problem needs to be corrected in the layout.js file. If you are in your wwwroot folder look under CFIDE/scripts/ajax/package directory. Once you have this file open you can make the changes as expressed by MrTee himself.

The change I made is on line 70 and changed from

var _70=new Ext.Panel({title:_6a,contentEl:_68,_cf_body:_68,id:_69,closable:_6c,tabTip:_6b, autoScroll:_6f,autoShow:true});

to

var _70=new Ext.Panel({title:_6a,contentEl:_68,_cf_body:_68,id:_69,closable:_6c,tabTip:_6b, autoScroll:_6f,autoShow:true,autoHeight:true});

I hope this helps anyone with the same problem out.

All in all an ‘autoHeight:true’ needs added to the Panel() method. As far as I know this has not been addressed in an update by Adobe, but has been acknowledged. I used this solution and it worked perfect with no known side-effects on my own applications.