Entries for month: February 2012
Revisiting To catch a shooting entityDelete()
Posted by Ryan Heldt in ColdFusion , SQL on February 2, 2012
About a year or so ago, I blogged about problems I was having with ColdFusion 9 ORM and using its entityDelete() function to delete items that ultimately could not be deleted because of foreign key constraints within the database. The problem I was having, in a nutshell, was I couldn't trap the error. Despite all kinds of error trapping, a nasty Hibernate exception kept bubbling up.
I was stuck. I tried posting the problem to cf-talk, but didn't find much help.
Some time later, I discovered the cf-orm-dev Google Group and again posted my quandary. Thankfully, I got several replies. One of those was from Mark Mandel (of Transfer and ColdSpring fame), someone in the CF community that I respect very much. He essentially said using try/catch in the manner I was attempting was bad coding practice. I had never considered that -- it was one of those things I always did. Another person suggested that I use ORM events, namely preDelete(). ORM events were new to me, so I investigated.
ColdFusion ORM includes a bunch of events which get called at various states of a database request's lifecycle. Think of it like Application.cfc for database calls. Among these include preLoad, postLoad, preInsert, postInsert, preUpdate, postUpdate, preDelete, and postDelete. Dan Vega has a really great breakdown of all these on his website. The thing I discovered early on is these events aren't enabled by default. You have to switch on eventhandling in ormsettings, like so:
this.ormsettings.eventhandling = true;
I decided that the preDelete() event would allow me to check for constrains, and if I found anything, eaisly back out of the delete. So, in order to delete something we have to load up a record, then run it through entityDelete():
objSong = ("Song",rc.songID,true);
entityDelete(local.objSong);
At this point, preDelete() gets called in Song.cfc, where I can check for any constraints:
<cffunction name="preDelete" access="public" output="false" returntype="boolean">
<cfquery name="local.qryChildRecords">
SELECT ServiceItemID AS ID FROM ServiceItems WHERE SongID = <cfqueryparam value="#this.getSongID()#" cfsqltype="cf_sql_integer" />
UNION
SELECT EventItemID AS ID FROM EventItems WHERE SongID = <cfqueryparam value="#this.getSongID()#" cfsqltype="cf_sql_integer" />
UNION
SELECT SongFileID AS ID FROM SongFiles WHERE SongID = <cfqueryparam value="#this.getSongID()#" cfsqltype="cf_sql_integer" />
</cfquery>
<cfif local.qryChildRecords.RecordCount>
<!--- Child records exists, abort the delete --->
<cfset this.setSongID(javaCast("null","")) />
<cfreturn true />
</cfif>
<cfreturn false />
</cffunction>
The way preDelete() works is if it returns a true value, the delete is aborted. While that's all fine and good, it doesn't do anything to inform the user nothing was deleted. So in order to detect that, I set the primary key of the object (in this case SongID) to NULL. Then, after entityDelete() is called I know if the delete happened or not. There may be a better way, but that seems to work for the vast majority of what I've done.
If anyone has any ideas or suggestions on how to improve on this, I'd love to hear them!
Friendly Filesize Formatting
Posted by Ryan Heldt in ColdFusion on February 2, 2012
Most ColdFusion functions, like cfdirectory, return file size information in bytes. While that is probably a more technically accurate number, it usually doesn't mean a whole heck of a lot to end users, who are probably more used to "denominations" such as the kilo or megabyte. I remedy this, I whipped up the following helper function.
<cffunction name="fileSizeFormat" access="public" returntype="string" output="false"> <cfargument name="size" type="numeric" required="true" /> <cfif arguments.size lt 1024> <cfreturn "#arguments.size# bytes" /> <cfelseif arguments.size lt 1024^2> <cfreturn "#round(arguments.size/1024)# KB" /> <cfelseif arguments.size lt 1024^3> <cfreturn "#decimalFormat(arguments.size/1024^2)# MB" /> <cfelse> <cfreturn "#decimalFormat(arguments.size/1024^3)# GB" /> </cfif> </cffunction>
Basic usage: fileSizeFormat(size)
If you need to show file sizes larger that GB, you could easily add 1024^4 and 1024^5 to the above if statement. Either that or seriously start looking into compression.