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

Popular posts from this blog

How to build this without writing Code

Connecting the Forms to the Data-Layer (part 9)

Connecting the Forms to the Data-Layer (part 8)