Single or stand-alone table (part 6)
Now we are getting to the true meat of the Module! Though SQL separates the Insertion from the Update, I feel it is NOT the Form code's responsibility to make this decision. The Goal is to remove this from the Form and put this decision into the Data Layer. Therefore, these two are combined into a single and simple Save Method.
The steps necessary to Insert and Update are the same, combining the is the only way to not have redundant code.
in pseudo code the steps are pretty straight forward:
If Validate Data = TRUE
IF New, then Insert
Update Data ID, set to Clean
ELSE Update
Set to Clean
ELSE Sent Error Message(s)
Let's take a Look at the Save Method, and then I'll walk you through it.
Procedure.i Save(*Class.FighterSize)
Protected Sql$, Result, DBID, Length, *MemoryGraphicID
Protected PassFail = #False
If Validate(*Class) = #False
ProcedureReturn #False
Else
DBID = DB::Open()
With *Class
SetDatabaseString(DBID, 0, \Name)
SetDatabaseString(DBID, 1, \UnitDescription)
SetDatabaseLong(DBID, 2, \UnitTypeID)
SetDatabaseLong(DBID, 3, \VisibleWithTechnologyLevelID)
SetDatabaseLong(DBID, 4, \IsHidden)
SetDatabaseLong(DBID, 5, \GameImageID)
SetDatabaseLong(DBID, 6, \Space)
SetDatabaseLong(DBID, 7, \ProdCost)
SetDatabaseLong(DBID, 8, \ProdTime)
SetDatabaseLong(DBID, 9, \Attack)
SetDatabaseLong(DBID, 10, \Defense)
EndWith
If *Class\State = DB::#New
Sql$ = "INSERT "
Sql$ = Sql$ + " into "
Sql$ = Sql$ + "FighterSize"
Sql$ = Sql$ + " ("
Sql$ = Sql$ + " Name, "
Sql$ = Sql$ + " UnitDescription, "
Sql$ = Sql$ + " UnitTypeID, "
Sql$ = Sql$ + " VisibleWithTechnologyLevelID, "
Sql$ = Sql$ + " IsHidden, "
Sql$ = Sql$ + " GameImageID, "
Sql$ = Sql$ + " Space, "
Sql$ = Sql$ + " ProdCost, "
Sql$ = Sql$ + " ProdTime, "
Sql$ = Sql$ + " Attack, "
Sql$ = Sql$ + " Defense"
Sql$ = Sql$ + " )"
Sql$ = Sql$ + " VALUES "
Sql$ = Sql$ + " (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ? )"
PassFail = DB::Update(DBID, Sql$)
If PassFail
*Class\State = DB::#Clean
*Class\ID = DB::getLastID(DBID, "FighterSize")
EndIf
DB::Close(DBID)
Else ;>>>>>> UPDATE <<<<<<
SetDatabaseLong(DBID, 11, *Class\ID)
Sql$ = "UPDATE "
Sql$ = Sql$ + "FighterSize "
Sql$ = Sql$ + "SET "
Sql$ = Sql$ + "Name = ? , "
Sql$ = Sql$ + "UnitDescription = ? , "
Sql$ = Sql$ + "UnitTypeID = ? , "
Sql$ = Sql$ + "VisibleWithTechnologyLevelID = ? , "
Sql$ = Sql$ + "IsHidden = ? , "
Sql$ = Sql$ + "GameImageID = ? , "
Sql$ = Sql$ + "Space = ? , "
Sql$ = Sql$ + "ProdCost = ? , "
Sql$ = Sql$ + "ProdTime = ? , "
Sql$ = Sql$ + "Attack = ? , "
Sql$ = Sql$ + "Defense = ? "
Sql$ = Sql$ + " WHERE "
Sql$ = Sql$ + "ID = ? "
PassFail = DB::Update(DBID, Sql$)
If PassFail
*Class\State = DB::#Clean
EndIf
DB::Close(DBID)
EndIf
EndIf
ProcedureReturn PassFail
EndProcedure
A standard call to the Save method should look like this:
If FighterSize::Save(@WorkFighterSize) = #True
If State = DB::#New ;insert record
ListEx::AddItem(#Gadget_frmList_liList,-1, WorkFighterSize\Name, "", 0)
ListEx::SetItemData(#Gadget_frmList_liList, ListEx::CountItems(#Gadget_frmList_liList)-1, WorkFighterSize\ID)
ListEx::SetState(#Gadget_frmList_liList , ListEx::CountItems(#Gadget_frmList_liList)-1)
Else ;update selected record
ListEx::SetItemText(#Gadget_frmList_liList, ListEx::GetState(#Gadget_frmList_liList), WorkFighterSize\Name,0)
EndIf
FighterSizeClose()
EndIf
I want to point out that data validation is done in the Data-layer, not in the form code. the form code only cares if the call to the save procedure was successful, if it was than the form can do what he needs to do and close.
The very first thing our Save Procedure does is validate the Passed in Structure or Object.
If Validate(*Class) = #False
ProcedureReturn #False
Else
DBID = DB::Open()
If the call to Validate Fails than he aborts any further processing and returns False to the Calling form code. We will touch on this when we get there, but I should mention that the displaying of errors is the job of the Validate Procedure, not the Save Procedure.
Continuing on with the Save Procedure, the next step, now that we know our data is valid is to Make a Call to our DB Module to open the database, and feed in our Values that our SQL will use in the Insert or Update.
With *Class
SetDatabaseString(DBID, 0, \Name)
SetDatabaseString(DBID, 1, \UnitDescription)
SetDatabaseLong(DBID, 2, \UnitTypeID)
SetDatabaseLong(DBID, 3, \VisibleWithTechnologyLevelID)
SetDatabaseLong(DBID, 4, \IsHidden)
SetDatabaseLong(DBID, 5, \GameImageID)
SetDatabaseLong(DBID, 6, \Space)
SetDatabaseLong(DBID, 7, \ProdCost)
SetDatabaseLong(DBID, 8, \ProdTime)
SetDatabaseLong(DBID, 9, \Attack)
SetDatabaseLong(DBID, 10, \Defense)
EndWith
Then Build our SQL. To know what SQL to build we need only look at the Structure's state Flag. If the State Value id DB::#New then we know this is an insert, otherwise it will be an Update. There are a couple important distinctions between Insert and Update Code
It is EXTREMELY important to order your fields in the SQL to Exactly match the order you entered them in Values section.
For the Insert We out SQL and use the "?" place holder for Pure Basic to supply the Value.
Sql$ = "INSERT "
Sql$ = Sql$ + " into "
Sql$ = Sql$ + "FighterSize"
Sql$ = Sql$ + " ("
Sql$ = Sql$ + " Name, "
Sql$ = Sql$ + " UnitDescription, "
Sql$ = Sql$ + " UnitTypeID, "
Sql$ = Sql$ + " VisibleWithTechnologyLevelID, "
Sql$ = Sql$ + " IsHidden, "
Sql$ = Sql$ + " GameImageID, "
Sql$ = Sql$ + " Space, "
Sql$ = Sql$ + " ProdCost, "
Sql$ = Sql$ + " ProdTime, "
Sql$ = Sql$ + " Attack, "
Sql$ = Sql$ + " Defense"
Sql$ = Sql$ + " )"
Sql$ = Sql$ + " VALUES "
Sql$ = Sql$ + " (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ? )"
If this is an Update we must FIRST provide the final Value, the Primary ID, that we are Updating, the we can build out the Update SQL Once again we will use the "?" place holder for PureBasic to supply the actual value.
SetDatabaseLong(DBID, 11, *Class\ID)
Sql$ = "UPDATE "
Sql$ = Sql$ + "FighterSize "
Sql$ = Sql$ + "SET "
Sql$ = Sql$ + "Name = ? , "
Sql$ = Sql$ + "UnitDescription = ? , "
Sql$ = Sql$ + "UnitTypeID = ? , "
Sql$ = Sql$ + "VisibleWithTechnologyLevelID = ? , "
Sql$ = Sql$ + "IsHidden = ? , "
Sql$ = Sql$ + "GameImageID = ? , "
Sql$ = Sql$ + "Space = ? , "
Sql$ = Sql$ + "ProdCost = ? , "
Sql$ = Sql$ + "ProdTime = ? , "
Sql$ = Sql$ + "Attack = ? , "
Sql$ = Sql$ + "Defense = ? "
Sql$ = Sql$ + " WHERE "
Sql$ = Sql$ + "ID = ? "
At this point whether we are Inserting or Updating our SQL$ Variable is ready for execution. We call our DB Module Update Method, to update our underlying database. There is a Very Important last step in the Insert execution. This is the call to our DB Module GetLastID to retrieve the ID of the record just inserted and update our Structure ID field with its Value.
If PassFail
*Class\State = DB::#Clean
*Class\ID = DB::getLastID(DBID, "FighterSize")
EndIf
the State of our Structure is set to DB::#Clean, as it now matches the database.
Finally, we close the Database, and return the results of our SQL Execution.
Comments
Post a Comment