Archive for March, 2010

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.

Coldfusion 9 getDataSource() now getStore()

Monday, March 29th, 2010

This doesn’t directly pertain to Coldfusion 9, but to the ExtJs library.  That being said, Coldfusion 8 runs off of an older version of the ExtJs library while Coldfusion 9 updated to the latest version 3 of the library.  If you are converting any Coldfusion 8 applications over to a Coldfusion 9 server keep in mind that your use of the Coldfusion AJAX components will no longer work for any components that looked for the underlying datasource.  So, for example, if you had a Coldfusion 8 grid implemented using the <cfgrid> tag and for whatever reason needed to grab some of the underlying ExtJs functionality of that grid by grabbing the grid datasource, you will need to make the update in your javascript.  Let’s pretend we have a grid named ‘mygrid’.  The Coldfusion 8 javascript code probably looked something like the following.

Coldfusion 8

//get our grid object
var mygrid = ColdFusion.Grid.getGridObject("mygrid");

//get the datasource of our grid
var mygrid_ds = mygrid.getDataSource();

//get data when the grids load
mygrid_ds.on("load", get_my_grid_data);

Once you move that code to the Coldfusion 9 server the ‘getDatasource()’ method will need to be changed to ‘getStore()’. So our new javascript would look like the following.

Coldfusion 9

//get our grid object
var mygrid = ColdFusion.Grid.getGridObject("mygrid");

//get the datasource of our grid
var mygrid_ds = mygrid.getStore();

//get data when the grids load
mygrid_ds.on("load", get_my_grid_data);

No idea why this change was made, but certainly no reason to panic. Simple fix.

MySQL Workbench doesn’t have Query Browser or Administrator in 5.1

Friday, March 19th, 2010

If you’re lazy like me and prefer to work in the mysql Administrator and Query Browser as opposed to the command prompt, you may be surprised if you go to the website now to download the GUI tools.  They have changed now to a single application called the Workbench.  This is great and a welcome change in my opinion.  The only problem I found was that they have removed the old GUI tools from the downloads page and have replaced it with the current stable release (5.1) of the Workbench.  This is fine except that the current stable release only contains the new data visualizer.  If you want the Workbench that contains the data visualizer and the Query Browser and Administrator you need to download the beta 5.2.  Unfortunately the beta is not readily available under the downloads page of the main site.  As such, the link to the download at the time of this writing is http://dev.mysql.com/downloads/workbench/.  So if you need the Query Browser and the Administrator you’ll need to download this version, or a later version depending on what is available at the time of your reading.

Entity has incorrect type for being called as a function in Coldfusion 8

Wednesday, March 17th, 2010

There’s some stuff on the web about this error in Coldfusion that I found helpful, but because it is such a useless error in the sense of telling you what the problem is I figured I would post my specific problem and solution.

In my case I was running a primary public function that called a few other private functions. The gist of my problem was that I had an argument in my primary public function with the name ‘generatepassword’. One of my private functions was also named ‘generatepassword’. When the private function was being called I suppose it wasn’t able to determine whether the argument or the method was to be called, and hence threw the error that really gives no explanation of this whatsoever.

I suppose that the ‘incorrect type’ is the argument that it was trying to call as a function. Who knows. Whatever the error may say, that was the problem. My search through google turned up similar cases. If you find yourself having this problem check your variable names and make sure that none of them conflict with your method names.

Random Password Generater UDF

Tuesday, March 16th, 2010

I realize that the web is full of password generators, but thought I’d add to the clutter with my own. It’s fairly simple with a few user-defined options for how the password is generated. Also you’ll need Coldfusion to run this method. Let’s take a quick look at the method, and as always feel free to use the method. Just drop it into a cfc, or on the page if that’s what fits your application best, call it with any special instructions via the arguments, and it will return back a struct with the appropriate data.

password_generator.cfc

<cfcomponent displayname="Generate Password" hint="Generates a random password." output="false">

	<!--- ************************  GENERATE PASSWORD  ************************ --->
	<cffunction name="generatePassword" displayname="Generate Password" description="Generates a random password." access="public" output="false" returntype="struct">

		<!--- ARGUMENTS --->
		<cfargument name="pwdlength" displayName="Password Length" type="numeric" default="10" required="false" />
		<cfargument name="includenumbers" displayName="Include Numbers" type="boolean" hint="Whether or not to include numbers in password." default="true" required="false" />
		<cfargument name="includeuppercase" displayname="Include Upper Case" type="boolean" hint="Whether or not to include uppercase characters." default="false" required="false" />
		<cfargument name="hashpwd" displayname="Hash Password" type="boolean" hint="Whether or not a hashed version of the password should be returned." default="false" required="false" />
		<cfargument name="hashtype" displayname="Hash Type" type="string" hint="Type of hash to perform." default="SHA-256" required="false" />

		<!--- SET CHARACTER RANGES --->
		<cfset var number = "48,57">
		<cfset var uppercase = "65,90">
		<cfset var lowercase = "97,122">

		<!--- SET A COUNTER --->
		<cfset var counter = 1>

		<!--- BUILD STRUCT --->
		<cfset var data = structnew()>
		<cfset data.passwordraw = "">
		<cfset data.passwordhashed = "">

		<cfscript>
			/* WHILE THE PASSWORD LENGTH IS LESS THAN THE SPECIFIED LENGTH */
			do
			{
				/* RETURN A RANDOM CHARACTER */
				character = randrange(listfirst(number, ","), listlast(lowercase, ","));

				/* IF THE CHARACTER IS LOWERCASE */
				if(character gte listfirst(lowercase, ",") AND character lte listlast(lowercase, ","))
				{
					/* SET CHARACTER INTO STRING */
					data.passwordraw = data.passwordraw & chr(character);
				}

				/* CAN WE USE UPPERCASE CHARACTERS */
				if(ARGUMENTS.includeuppercase eq true)
				{
					/* IF CHARACTER IS UPPERCASE */
					if(character gte listfirst(uppercase, ",") AND character lte listlast(uppercase, ","))
					{
						/* SET CHARACTER INTO STRING */
						data.passwordraw = data.passwordraw & chr(character);
					}
				}

				/* CAN WE USE NUMBERS */
				if(ARGUMENTS.includenumbers eq true)
				{
					/* IF CHARACTER IS NUMBER */
					if(character gte listfirst(number, ",") AND character lte listlast(number, ","))
					{
						/* SET CHARACTER INTO STRING */
						data.passwordraw = data.passwordraw & chr(character);
					}
				}
			}
			while(len(data.passwordraw) lt ARGUMENTS.pwdlength);
		</cfscript>

		<!--- SHOULD WE HASH THE PASSWORD --->
		<cfif ARGUMENTS.hashpwd eq true>
			<cfset data.passwordhashed = hash(data.passwordraw, ARGUMENTS.hashtype, "us-ascii")>
		</cfif>

		<cfreturn data>

	</cffunction>

</cfcomponent>

It takes five arguments. First is the length of the password you want to generate. So do you want it to be 4 characters, 10 characters, or whatever. Second, do you want numbers to be included in the password? If so set it to true. This defaults to true if not specified. Third, do you want any letters that are uppercased? If using a case sensitive password you may wish to specify this to true. It defaults to false. Fourth, do you want to also return a hashed version of the password. If storing in a database you may want this. And if you are returning a hashed version, which algorithm should be used in the hash process. This defaults to “SHA-256″. You can use any algorithm supported by Coldfusion.

Next I set some variables that hold the lowest and highest byte code representing numbers, uppercase, and lowercase according to ascii. Build the struct to return, and then set some script that will pump out our random password according to the specifications.

Lastly, if the password should be hashed it is.

Enjoy.

Mapping a table using ORM in Coldfusion 9

Saturday, March 13th, 2010

I’ll assume here that you have already set ORM in your Application.cfc.  If not you may want to check out the post on setting up ORM in the Application.cfc file first.  If you have the Application file all set and you have a database then you’re ready to map out a database table.

For this example I’ll be using MySql as the database with a table called ‘stores_tbl’.  It has three fields that we need to worry about.  The first is the ‘store_id’ (the primary key), the ‘store_name’, and the ‘store_link’.

We’ll need three files to make this work.  First is the Application.cfc file, which again I’m assuming is already set up.  Second is a .cfm file that will output our data objects.  And third is the persistent cfc that will map to the ‘stores_tbl’.  I’ve created a directory called ‘orm_mappings’ under my root directory to hold all my persistent cfcs.   You can also create a directory if you like.  When the application loads Coldfusion searches through the application looking for any persistent cfcs and maps them accordingly.  So technically you could organize your persistent cfcs any way you wish as long as they are inside the application using ORM.

Let’s take a look at our persistent cfc.

stores_tbl.cfc

<cfcomponent displayname="Stores Table" hint="ORM mappings to the stores_tbl" output="false" persistent="true">

	<!--- SET PRIMARY KEY COLUMN --->
	<cfproperty name="id" column="store_id" ormtype="integer" generator="native">

	<!--- SET OTHER COLUMNS --->
	<cfproperty name="store_name" ormtype="string">
	<cfproperty name="store_link" ormtype="string">

</cfcomponent>

First thing to note here is the name of the cfc. It is named the same as the table name it is mapping to. So our table name is ‘stores_tbl’ and the name of the cfc is ‘stores_tbl.cfc’. The name of the cfc will indicate to Coldfusion where the mapping is to take place. Next is the ‘persistent=”true”‘ attribute in the ‘cfcomponent’ tag. This indicates to coldfusion that this is an ORM mapping and not a standard cfc.

(more…)

Setting up ORM in Coldfusion 9

Saturday, March 13th, 2010

First things first, you will need a database and Coldfusion 9 for this to work.  Once you have  these two things you’ll need to create your Application.cfc file.

Remember in Coldfusion 8 using the THIS scope at the top of the component outside of any functions to set things like the name of the application, the session management, and the application timeout.  Well, now you need to add to that list a few new settings in the THIS scope.  You’ll need to add THIS.ormenabled = true and THIS.datasource = “yourdatasource”.

So our very simple Application.cfc file will look like the following.

Application.cfc

<cfcomponent displayname="Application File" hint="Application File" output="false">

	<!--- SET UP ORM --->
	<cfset THIS.name = "applicationTest">
	<cfset THIS.datasource = "datasource">
	<cfset THIS.ormenabled = true>

</cfcomponent>

Keep in mind that setting the datasource in the THIS scope is the same thing you did in Coldfusion 8 when you would set the datasource in the tag. Point being that this datasource is pretty useless without setting up the datasource in the Coldfusion Administrator first. Take a look at Setting up a datasource to learn more about this.  The really freakin awesome thing about this though is that setting the datasource here sets it globally across your application, which means for every call to the database you do not need to specify the datasource.  This is a big deal in my book.

So ORM should be all set in your application.

DISCLAIMER** This is a record of what I’m learning in ORM in Coldfusion 9. While what I post should work, I lay no claim to best practices. My hope here is that other ORM beginners like myself may benefit from this information on the assumption that their path to learning ORM might be similar to my own. To learn more about ORM by those who know more than myself check out (of course) the adobe docs and a couple very nice presentations on cfmeetup.com by Raymond Camden and Bob Silverberg.

Setting up a datasource in Coldfusion Administrator

Saturday, March 13th, 2010

Quick post on how to set up a datasource in the Coldfusion Administrator. First you’ll want to open the Coldfusion Administrator. I’m going to assume that you will be doing this locally since if you don’t have a datasource you’ve probably are just at the beginning stages of development. As such let’s open the admin.

Working locally it is likely that you will use the following link http://localhost:8500/CFIDE/administrator/index.cfm for Coldfusion 8 and http://127.0.0.1:8500/CFIDE/administrator/index.cfm for Coldfusion 9. Remember though if you have multiple versions of the Coldfusion server running on your machine the port number may not be 8500. Instead as ports are used Coldfusion will increment the port number by 1 until reaching the next available port. So for example if you had Coldfusion 8 running, and are now running Coldfusion 9 as well your port number is most likely 8501. Nonetheless, if this is the case, my guess is that your not reading this article.

Once into the administrator click on ‘Data Sources’ under the ‘Data & Services’ tab on the left.

Data Sources menu in Coldfusion 9 Administrator

Next create a name for your datasource.  This can be whatever you want it to be.  This is the label that will represent the connection setup between Coldfusion and this particular database.  You will also need to select a driver from the drop-down.  In my case I’m using MySql 5 so I’ll choose ‘MySql 4/5′ from the drop-down.  You will choose whatever is appropriate for your particular database.

(more…)

Converting a string to an array of characters

Monday, March 8th, 2010

Just thought I’d write up a quick post on converting a string of characters to an array of characters.  I’ve never had any difficulty doing this using Java’s getBytes() method which converts a given string to an array of it’s equivalent byte code.  Nonetheless I just stumbled upon a native Coldfusion method that does the same exact thing without having to directly access the underlying Java.

To convert a string to an array of character bytes using Coldfusion simply use the charsetdecode() method.  You can then loop over the byte array and encode each character using the chr() method, and set into a new array.  In the end you will end up with an array of the characters including white spaces and all.  Also any string you pass through the charsetdecode() method you can pass back through the charsetencode() method to convert it back into its original string.  A quick example of this is below.

<!--- SET A STRING --->
<cfset VARIABLES.myname = "My name is Matthew.">

<!--- CONVERT TO AN ARRAY OF BYTES --->
<cfset VARIABLES.bytes = charsetdecode(VARIABLES.myname, "us-ascii")>

<!--- CREATE AN EMPTY ARRAY --->
<cfset VARIABLES.data = arrayNew(1)>

<!--- LOOP OVER ARRAY OF BYTE CODE --->
<cfloop array="#VARIABLES.bytes#" index="VARIABLES.i">
	<!--- SET EACH CHARACTER INTO THE ARRAY --->
	<cfset arrayAppend(VARIABLES.data, chr(VARIABLES.i))>
</cfloop>

<!--- DUMP THE ARRAY --->
<cfdump var="#VARIABLES.data#">

It outputs the following.

Coldfusion Character Array

It can now be treated as any array. What else have I been overlooking?