(/services/dealerService.cfc )
public function createDealer( id ){
/* I create and return a domain object */
}
public function saveDealer( rc ){
var dealer = 0;
var results = 0;
/* dealer domain object can populate from
a supplied structure */
dealer.populate( arguments );
/* dealer domain object can check itself
for errors */
dealer.validate();
if (! dealer.hasErrors() ){
/* dealer can save itself too!*/
dealer.save();
}
return dealer;
}
public function editDealer( rc ){
var errors = 0;
var dealer = 0;
if ( structKeyExists( arguments, 'dealer' ) && isObject(arguments.dealer) ){
/* We most likely got here as the result of a form submit and redirect.
pass the object along again */
dealer = arguments.dealer;
} else if ( structKeyExists( arguments, 'dealer_id')
/* We're here from a selection somewhere else, and are editing a
dealer record for the first time. instantiate the object to pass on. */
dealer = createDealer( id=arguments.dealer_id );
} else {
/* We're most likely here to create a new dealer record. Instantiate an
empty object. The save() function in the object knows how to insert
a new record when the id=0 */
dealer = createDealer( id=0 );
}
return dealer;
}
(/Controllers/dealer.cfc)
public function endSaveDealer( rc ){
var message = 'Your data was saved successfully';
var persistList = 'message';
var dealer = 0;
if ( structKeyExists( arguments.rc, 'data' )){
dealer = rc.data;
persistList = 'all';
if ( dealer.isDirty() && dealer.hasErrors() ){
message = 'not saved, there were errors in your data...';
}
/* place the correct message in the rc scope */
arguments.rc.message = message;
arguments.rc.dealer = dealer;
} else {
arguments.rc.message = 'Something happened and your data was lost.';
}
/* return to the edit item with one of three options.
1. the data was saved, and a message will be passed on
stating so. the saved, clean dealer object will also
exists in the rc scope.
2. the data was not saved, and it still contains the dirty
data that the user needs to edit. Pass it back in to
the rc scope, along with a message.
3. Somehow we got here without the rc.data variable.. while
that shouldn't have happened, there isn't much we can do
about it.. so send them back to the form with a message.
a new dealer object will be created in the editDealer
service function
*/
structDelete(arguments.rc, data);
variables.fw.redirect( 'dealer.editDealer', persistList );
}
( dealer/editDealer.cfm view )
<cfscript>
if ( isDefined('rc.data') && isObject('rc.data') ){
local.dealer = rc.data;
} else {
/* means we didn't get the service layer dealer object we should have.
This is how I like to handle all the outputs below without having
to validate them all in place. If I don't have an object (even an empty
one) then throw an error. */
raiseException( type="FW1.serviceExecutionComplete", message="dealer domain object not found.",
detail="In order to populate an edit form, I expect a dealer domain object to be sent in from the service layer as rc.data" );
}
</cfscript>
<cfif isDefined('rc.message')>#rc.message#</cfif>
<form action='index.cfm?action=dealer.saveDealer'>
<input type='hidden' name='id' id='id' value='#local.dealer.getDealer_id()#' />
<input type='text' name='dealer_id' id='dealer_id' value='#local.dealer.getDealer_id()#' />
<input type='text' name='address' id='address' value='#local.dealer.getAddress()#' />
<input type='text' name='city' id='city' value='#local.dealer.getCity()#' />
</form>
So, here's my usual logic... because FW1 will automatically call a matching service item, I don't usually have to create any controller functions, other than the endItem functions that usually occur after some type of form submit. Since I dont' create views for those functions, I want to return them to the appropriate place. That's usually right back to the page they came from, so they can either continue editing stuff, or fix the errors.
When someone first comes to the dealer edit form, they are either here to edit, or create a record. If they clicked a link that passed an id, that record is instantiated and passed in to the view to populate the field values. If the id was 0 or not there, it creates the record for the first time and sends them in to the form to add the data.
When they post to the saveitem, FW1 appends the RC scope to the argumentsCollection when it invokes the service. There is no need for a controller, we already know we're going to try to save some data.. so just expect that the matching service function will handle the saving of the record. Once the services are complete. FW1 calls the endXxX() function. So, I create that function to handle what to do "next" depending on what happened during the save.
If the object that was passed has errors, we redirect to the editForm with that "same" object and it doesn't need to get instantiated again in the service layer.. it just renames it and passes it back on again.. The message of the error was appended to the rc scope in the endXxX() controller and it will exists in the view.
If the object did not have errors, it still renames it and sends it back along in the RC scope because I typically go back to the form after a data save, so they can decide what they want to do next, or make more changes.
If the object did "not" exist in the endXxX() function (for some odd reason), we redirect back through to the edit form but we don't pass in our object, and it knows that it needs to create one.. and the RC scope message is there for the user.
I typically try and leverage my domain objects to know how to save themselves, validate themselves, etc.. that way I don't have to create totally custom code for them..
This is how I've been using FW1.. and it's worked really really well.. And if I'm doing everything right, you can thank Sean, I've spent many sleepless nights picking his brain.. and never once has he told me to go to hell :) If everything above is completely wrong and makes no sense at all.. you can blame Sean, it would naturally be his fault that I didn't learn anything on all those sleepless nights!
(forgive any typos, or syntax errors.. I'm typing, not coding/compiling )
Shane