Following up on my blog entry from yesterday, today I'm going to talk about how to install Transfer and what my simple little application is going to look like.
Installation, like with most frameworks, consists of simply downloading a zip, extracting, and either placing the files under your application or creating a ColdFusion mapping. You can find download links here. This is no different than ColdSpring, Model-Glue, etc, but I really like to make use of ColdFusion 8's new ability to define mappings on the fly. So with that in mind, I placed Transfer in web root in a folder named transer1.1.
My application will be placed in a folder named empdirectory. I'm putting this right under web root as well. I'm not going to use Model-Glue or any other framework for this application as I want things to be as simple as possible. Let's first look at my Application.cfc file, specifically the mappings I'll use:
<cfset this.mappings["/empdir"] = getDirectoryFromPath(getCurrentTemplatePath())>
<cfset this.mappings["/transfer"] = expandPath("../transfer1.1/")>
The first line defines a mapping named empdir. I'll use this as my root mapping for the application. This isn't something Transfer needs but is a convenience that will help in other places as well. Since Transfer is also directly below web root, I used ../transfer1.1 to point to the folder where I extracted the files. I don't know about you guys, but I freaking love being able to do mappings on the fly!
So now for the most important part. How do we add Transfer to our application? The installation docs tell us we begin by creating an instance of a CFC named TransferFactory. This CFC takes 3 main arguments when initialized. Let me show you how I created it within my application and then I'll describe each argument.
<cfset application.transferFactory =
createObject("component", "transfer.TransferFactory").init(
"/empdir/configs/datasource.xml",
"/empdir/configs/transfer.xml",
"/empdir/configs/definitions")>
The first argument tells Transfer where it will find datasource information. As you can imagine, since Transfer is abstracting interactions with your database, it needs to know which ColdFusion datasource to communicate with. I created a datasource.xml file and placed it within a config folder under my applications folder. My file has the following XML:
<datasource>
<name>employeedirectory</name>
<username></username>
<password></password>
</datasource>
Note! You must use the username and password keys even if you don't need them. I supplied this information in the ColdFusion DSN so I didn't need to supply it here.
The second argument is my Transfer Configuration file. This is a critical file. This file defines everything about how Transfer will abstract your database access and covers other items like caching as well. So basically, when I want Transfer to let me work with Employee objects, this is where I'll help define that relationship between the Employee business object and the database columns in the back end.
I am not going to cover the entire dialect of this XML file tonight! Tonight we will start simple. Here is the XML file as it stands in the first version of the application.
<transfer>
<objectDefinitions>
<object name="employee" table="employees">
<id name="id" type="numeric" />
<property name="firstName" type="string" />
<property name="lastName" type="string" />
<property name="dob" type="date" />
<property name="email" type="string" />
<property name="phone" type="string" />
</object>
</objectDefinitions>
</transfer>
As you can probably guess, the objectDefinitions block defines what objects Transfer will recognize within my application. Right now my application has one object - employee. Because my database uses a different name for the table (employees) I have to tell Transfer what the table name is. My employee is made up of a few properties. I used some fairly generic things like first and last name, dob (date of birth), email and phone. I could have more of course, but again, I'm keeping things simple. Note that for each property I tell Transfer what type of property is being modeled. The only unique item here is the id tag. This tells Transfer what the primary key is for my database table.
So again - I'm not describing nearly every thing you can do in this file, but tonight, I want to keep it simple. (And I'll have complete zips at the end so you can see everything yourself!)
So finally, the last argument you pass to the factory is a folder name. This is a folder that Transfer will use for cached data and other generated files. I pointed to a definitions folder I created under config.
From this moment on, when it comes to Transfer, the main file I'll work with is the transfer.xml file. As I add new business object, define relationships, etc., I'll be workin gin there. I really have no need to touch datasource.xml file or worry about the definitions folder.
Ok, so now for the last item that may be confusing to folks. We just made an instance of the TransferFactory. But that isn't actually what you will use for most of your work. Instead, you will make use of a Transfer CFC that comes from the factory. In the Transfer docs you may see many examples that look like this:
<cfset foo = application.transferFactory.getTransfer().new("foo")>
This can get pretty tiring pretty quickly, so I simply created an application cached version of this component as well. Here is the entire onApplicationStart:
<cffunction name="onApplicationStart" returnType="boolean" output="false">
<cfset application.transferFactory =
createObject("component",
"transfer.TransferFactory").init(
"/empdir/configs/datasource.xml",
"/empdir/configs/transfer.xml",
"/empdir/configs/definitions")>
<cfset application.transfer = application.transferFactory.getTransfer()>
<cfreturn true>
</cffunction>
While the Factory CFC does have functions we will use (and I'll be showing that in a second), it's the Transfer CFC itself that I'll spend most of my time in.
I'm a big fan of taking baby steps, so before I did anything else, I ran my application (using an empty index.cfm file) just to ensure nothing threw an error. Once that worked, I then used this code:
<cfoutput><p/>Transfer version: #application.transferFactory.getVersion()#</cfoutput>
This returned: Transfer version: 1.1
Wow, that was a lot of typing and I really haven't accomplished anything yet. But as I said, I like to go slow and ensure that the framework can even load without me screwing it up, and so far, it has.
Alright - so everyone with me so far? In the next blog entry I'll actually start using the Employee object with a very simple (and awesomely ugly - yes - I make web sites ugly) CRUD.
Archived Comments
Transfer is change we need.
Nice... ;)
Really looking forward to the rest of this series!
btw congrats on your new president, good choice :)
Great post, Ray. I think going through installing/using Transfer in baby steps like you're planning will be great for those interested in trying it out but afraid it's more complicated than it actually is.
Ray. You said "Because my database uses a different name for the table (employees) I have to tell Transfer what the table name is."
Have you already made your database tabes? I thought Transfer would make the tables for you?
Unless I'm missing something...
Geoff, I kinda skipped over that - and I _knew_ that was a mistake, but I was hoping to get back to it tonight.
As a writer, I should have learned by now - trust my instincts.
No - Transfer will NOT make your tables for you. Hibernate does, and that is _extremely_ cool, but in this case, I had to make the table myself.
In tonight's blog entry, I'm discussing basic CRUD operations, and I'll be sure to cover this (not everyone reads the comments).
Cool?
Thanks for calling me out on that!
Great idea Ray, looking forward to the entire series.
If anyone is too impatient, like myself, to wait for the next post, here is the script for making the table in MySql.
-- ----------------------------
CREATE TABLE `employees` (
`id` int(11) default NULL,
`firstname` varchar(50) default NULL,
`lastname` varchar(50) default NULL,
`dob` date default NULL,
`email` varchar(255) default NULL,
`phone` varchar(50) default NULL
) ENGINE=MyISAM DEFAULT CHARSET=latin1;
-- ----------------------------
just make a db, create this table and then point a cf dsn named 'employeedirectory' at it.
Good luck.
@Geoff - I believe your question about whether Transfer creates tables or not (and the question behind the question) is one of the reasons Ray is starting this series.
Some folks don't believe Transfer is a true ORM since it will not create (or update) your database based on your objects. You can read more about the different perspectives on this topic in the following blog posts and comments:
Joe Rinehart:<br/>
http://firemoss.com/post.cf...
http://www.firemoss.com/pos...
Brian Kotek:<br/>
http://www.briankotek.com/b...
@AW: Well technically no. That's not why I started the series. If you see the first entry (see Related Entries above), I definitely talk about those articles by Joe and Brian, but my main intent here is to help others learn Transfer, and to help myself get a better understanding as well. (I'm most interested in the Event stuff. I have no idea how it's used so I'm looking forward to that.)
I think Joe's article is a good one. It certainly helped me. But this series main goal is more on the practical side.
Anyone having trouble running the code on Railo 3? I keep getting "invalid component definition, can't find transfer.TransferFactory" but my mappings look correct.
Right before the createObject, do a cfoutput on expandPath("/transfer"), and ensure that a) it points to the right file, and b) that folder has TransferFactory.cfc in it it.
Yup, checked both before and looks correct.
@Chris - if you get rid of my fancy this.mappings (well, don't get rid of it, remove the line for transfer) and make a 'real' CF mapping, does it work?
I'd also encourage you to post to the Railo listserv. They are typically fairly good with this kinda stuff.
Well, I removed the mappings and went to the railo admin and set the mappings there (A little different than CF8). Still nothing. I will try posting there too and post what I find.
Any help with this error?
The XMLFILEREADER argument passed to the init function is not of type transfer.com.io.XMLFileReader.
That's a new one on me. Maybe a typo in your XML? Unless you used the exact same XML from the zip. Worse comes to worse, try the mailing list: http://groups.google.com/gr...
I had a copy of the \Transfer folder in the employeedirectory. I removed it and everything is fine. Thanks