Slides for ORM presentation and a correction on Inverse

CFUnited, ColdFusion 9, ORM

I really enjoyed giving my 15 things you should know about ORM presentation at CFUnited.  Here are the slides:

 

Bob Silverburg came to me afterwards and corrected some of the information I had presented about the inverse property settings for relationships.  Here is my latest attempt at explaining inverse:

  • it only impacts bi-directional relationships
  • both sides of the realtionship get add, has, remove functions
  • both sides of the relationship should be maintained with the above functions
  • Hibernate, though, will run two update statements.  This is the need for setting inverse=true on the 'one-to-many' side.  This will will tell Hibernate to only run one update statement.

 

Example of multiple datasources for ORM in 9.0.1

ColdFusion 9, ORM, ColdFusion 9.0.1

ColdFusion 9.0.1 brings multiple datasources support for ORM. Here is how to set it up.

In this example, we are going to use both the cfartgallery and cfbookclub datasources.  In the Application.cfc we will make the cfartgallery the default datasource.  Optionally we are going to set the dbcreate rules differently for the two datasources.  (We could also do this for the schema, catalog, dialect and sqlscript settings.)

 

component
{
this.name="multi";
this.ormEnabled=true;
this.datasource="cfartgallery";
this.ormsettings = { dbcreate={ cfartgallery="update",
cfbookclub="none"} };
}

 

From cfartgallery we will use the Art table as an object.  As cfartgallery is the default datasource, this is set up as usual:

 

component persistent="true"
{
property name="artID" fieldtype="id" ;
property name="artistID";
property name="artname";
property name="description";
property name="prize";
property name="largeimage";
property name="mediaID";
property name="issold";
property name="samColumn";
}

 

For any tables from the cfbookclub datasource there is an additional attribute "datasource" for the component:

 

component persistent="true" datasource="cfbookclub"
{
property name="bookID" fieldtype="id";
property name="authorID";
property name="title";
property name="bookdescription";
property name="isspotlight";
}

 

We can then get the data and dump it like so:

 

books = entityLoad("BOOKS", {}, {maxResults=2});
writeDump(books);
art = entityLoad("ART", {}, {maxResults=2});
writeDump(art);

 

 Pretty straighforward and pretty cool stuff.

Thoughts on building with a ColdFusion Builder extension with Flex

ColdFusion Builder, ColdFusion 9

I thought Builder extensions operated in a funny web browser that meant you could use html, including forms, but returned data in some funny XML format.  Oh how wrong I was.  You can use html but only for outputting information.  The most common way to write extensions is by using the built in Eclipse input types which pass data to ColdFusion via that funny XML format I spoke about earlier.  They are great if you are looking to tie into Eclipse but I wanted some UI tools to list data, a textarea to edit and a nice chart.

[UPDATE 6/30/10: There is an easier way.  Read this blog entry]

So, it was on to using Flex to build a front end that had the UI controls I needed and started by reading this great blog post by Terry Ryan.

This method works by writing a CFC webservice exposed via WSDL to pass data back and forth.  Terry used XML as a data transformation protocol.  Now, for me, XML is like warm beer.  Its there to be drunk, but first, lets see if there's a cold one around.  I decided to go with using cf queries to pass data around.  Combined with the Data Services panel in Flex Builder 4 this made the data transmission easy.

One drawback to this approach is that Data Services assumes the URL for the webservice will not change.  To get around this I we need to pass that path to Flex via URL variables when we call the swf from within our extension.  Don't worry its actually simpler than that sounds.

Starting on the Flex side, I opened up the generated services file and removed any mention of the wsdl path.  This gets replaced with what is passed in during the declaration of the service, which I bind to a variable that is set based on a function called during preinitialize.  Here's the code:

<s:Application xmlns:fx="http://ns.adobe.com/mxml/2009"

                  xmlns:s="library://ns.adobe.com/flex/spark"
                  xmlns:mx="library://ns.adobe.com/flex/mx"
                  xmlns:service="services.service.*"
                  preinitialize="init()">
                 
<fx:Script>
<![CDATA[
     [Bindable]
     public var wsdlPath:String = "";
    
     protected function init():void
     {
          wsdlPath = this.parameters.wsdlPath;
     }
]]>
</fx:Script>
<fx:Declarations>
     <service:Service id="service" wsdl="{wsdlPath}" useProxy="false" />
</fx:Declarations>

Thats a slightly simplified version.  All the code is available in the subversion repository.  (If you are a Flex developer and you know a better way please let me know.  I'm a newbie when it comes to Flex.)

On the ColdFusion side we need to pass in the server path to the swf.  Here's code for that, loosely based on Terry's code.

 

<cfscript>
if ( ! len( cgi.HTTPS ) ) {
     serverRoot = "http://" & cgi.HTTP_HOST & ":" & cgi.SERVER_PORT;
} else {
     serverRoot = "https://" & cgi.HTTP_HOST & ":" & cgi.SERVER_PORT_SECURE;
}
serverRoot &= getDirectoryFromPath( cgi.script_name );
wsdlPath = serverRoot & "Service.cfc?wsdl";
flashPath = serverRoot & "flash/VFSe.swf?wsdlPath=#urlEncodedFormat( wsdlPath )#";
</cfscript>    
 
<cfcontent reset="true">
<cfheader name="Content-Type" value="text/xml">
<cfoutput>
<response showresponse="true">
     <ide url="#flashPath#" >
          <dialog title="All these files are virtual..." width="1000" height="1000" />
     </ide>
</response>
</cfoutput>

 

Overall I found building an extension with Flex pretty straightforward and just by using the default Spark look the UI is pretty attractive.

Virtual File Explorer Released! ColdFusion Builder Extension to view, edit and delete your virtual files and directories.

ColdFusion 9, Virtual File Explorer

The addition of a virtual file system, essentially RAM based files, in ColdFusion 9 is a very cool one and very useful at times.  Working with the virtual file system can be problematic though as, well, its virtual and therefore hard to see!

Enter the ColdFusion Builder extension called Virtual File Explorer that provides this insight.

The image above is shrunk so you'll have to download and install the extension to see its true beauty.  The interface above shows the following features:

  • Pie chart of free and used space
  • List of all directories with total size, files with file size
  • Delete button for deleting a file or directory
  • Edit button for viewing or editing a file (or double-click).

The edit file interface is basic but functional:

I've tagged this as version 0.8.  I have some ideas for what would be nice for version 1 but I'd also love to hear from you for any ideas or problems you come across. Thanks also to Raymond Camden for testing and providing great feedback. 

Download Virtual File Explorer now!

Later in the week I'll blog my thoughts about building an extension.

Liking using implicit arguments for ColdFusion 9 functions

ColdFusion 9

Each new version of a computer language inevitably brings about changes, mostly for the better, in the style of writing that language.

One change I have enjoyed with ColdFusion 9 is using implicit structures as arguments to a function.  This leads to bundling of arguments into structures in cases where it makes more sense.  Granted this has been going on in languages like JavaScript (and even the ColdFusion Javascript functions!) for a long time.  There are definitely good and bad reasons to use this style.

Lets take a function, written with arguments individually defined, and see how this works:

function getAndCache( url, cacheType, cacheTime, cacheContent )

That can be rewritten to:

function getAndCache( url, cache )

This time we put all the cache settings in the cache argument.

Advantages:

  • All cache settings are put in one structure.
  • These arguments can potentially be defined in one application wide place and passed in easily (in a similar way that argumentCollection can be used).

Disadvantages:

  • Slightly harder to document and validate

As I mentioned earlier I'm not suggesting using this for every function but for those that have a collection of config or style options it can work well.  This is seen in a lot of JavaScript functions and used widely by jQuery.

Infinite entities, cfgrid and one cfc to handle the data

ColdFusion 9, ORM

While writing SpreadEdit I wanted every entity to be editable via cfgrid.  In particular I thought it would be cool to have one cfc on the back end to process the data from cfgrid no matter what entity it was working with.  With ColdFusion 9 and ORM this proved possible and is pretty cool, check out the screencast.  Code the other side of the embed.

Here is the code for genericGrid.cfc:

<cfcomponent>
 
<cffunction name="getData" access="remote" returnformat="JSON">
     <cfargument name="page" required="true">
     <cfargument name="pageSize" required="true">
     <cfargument name="gridSortColumn" required="true">
     <cfargument name="gridSortDirection" required="true">
     <cfargument name="entity" required="true">
 
     <cfif ! len( arguments.gridSortColumn )>
          <cfset arguments.gridSortColumn = "1">
 
     </cfif>
 
     <cfset local.getTasks = ormExecuteQuery( " FROM #arguments.entity# ORDER BY #arguments.gridSortColumn# #arguments.gridSortDirection#" )>
 
    <cfreturn queryconvertforgrid( entityToQuery( local.getTasks ), Arguments.page, Arguments.pageSize)>
 
</cffunction>
 
<cffunction name="setData" access="remote">
     <cfargument name="action" required="true">
     <cfargument name="row" required="true">
     <cfargument name="changed" required="true">
     <cfargument name="entity" required="true">
     <cfif arguments.action eq "U">
          <cfset local.obj = entityLoad( arguments.entity, arguments.row, true )>
          <cfloop collection="#arguments.changed#" item="local.key">
               <cfinvoke component="#local.obj#" method="set#local.key#">
                    <cfinvokeargument name="#local.key#" value="#arguments.changed[ local.key ]#">
               </cfinvoke>
          </cfloop>
     <cfelseif arguments.action eq "I">
          <cfset local.obj = entityNew( arguments.entity )>
          <cfloop collection="#arguments.row#" item="local.key">
               <cfif local.key neq "CFGRIDROWINDEX">
                    <cfinvoke component="#local.obj#" method="set#local.key#">
                         <cfinvokeargument name="#local.key#" value="#cleaned( arguments.row[ local.key ] )#">
                    </cfinvoke>
               </cfif>
          </cfloop>
     </cfif>
     <cfset entitySave( local.obj )>
</cffunction>
 
<cffunction name="cleaned" access="private" >
     <cfargument name="string" required="true">
     <cfset var ret = arguments.string>
     <!--- check for date --->
     <cfif reFind( "[[:digit:]]{4}-[[:digit:]]{2}-[[:digit:]]{2}T[[:digit:]]{2}:[[:digit:]]{2}:[[:digit:]]{2}",
               ret )>
          <cfset ret = rereplaceNoCase( ret, "T", " " )>
     </cfif>
     <cfreturn ret>
</cffunction>
 
 
</cfcomponent>

Search

Twitter
You should follow me on Twitter here
About Me
I am a 34-year old Web Developer specializing in ColdFusion. I live and work in downtown Washington, DC with my wife and two daughters. Read more About Me

2007 CFeMmy Best Newcommer winner
As voted on by fellow CF Bloggers.