In the following example I'll build a Visual FoxPro form that uses the cDeveloper class to retrieve the data from SQL Server over the Web. This involves a quick three step process:
************************************************************************
* HTTP :: HTTPSQL
****************************************
FUNCTION HTTPSQL_wwDevRegistry()
*** Create Data Object and call Server Side Execute method (wrapper for Process Method)
SET PROCEDURE TO wwHTTPSQLServer ADDITIVE
loData = CREATE("wwHTTPSQLServer")
loData.cConnectString = "server=(local);driver={SQL Server};database=wwDeveloper;pwd=sa;uid=;"
loData.cAllowedCommands = "select,execute,insert,update,delete,method,"
*** Retrieve XML input and then try to execute the SQL
loData.S_Execute(Request.FormXML())
*** Send the output back to the client
loHeader = CREATEOBJECT("wwHTTPHeader")
loHeader.SetProtocol()
loHeader.SetContentType("text/xml")
loHeader.AddForceReload()
loHeader.AddHeader("Content-length",TRANSFORM(LEN(loData.cResponseXML)))
Response.Write( loHeader.GetOutput() )
Response.Write( loData.cResponseXML )
ENDFUNC
* HTTP :: HTTPSQL_wwDevRegistry
That's all it really takes.
For more detailed info on how to configure the server side for reusable SQL connections and security check out How wwHTTPSQLServer works.
DO WCONNECT
SET CLASSLIB TO wwDeveloper Additive
SET PROCEDURE TO wwHTTPSQL Additive
oDev = CREATEOBJECT("cDeveloper")
oDev.nDataMode = 4
oDev.cServerUrl = "http://localhost/wconnect/wc.dll?http~HTTPSQL_wwDevRegistry"
*** Sets up the HTTP object so we can configure it (optional)
oDev.Open()
oDev.oHTTPSQL.nConnectTimeout = 40
*oDev.oHTTPSQL.cUsername = "rick"
*oDev.oHTTPSQL.cPassword = "keepguessingbuddy"
? oDev.Query() && Retrieve all records
? oDev.cErrorMsg
BROWSE
This should show you all the records from the server. Note that this data was retrieved from the Web Server, not from the local SQL Server. All operations that you could perform on the business object before still work as you would expect with data coming over the Web:
*** Load one object
oDev.Load(8)
? oDev.oData.Company
? oDev.oData.Name
oDev.oData.Company = "West Wind Technologies"
? oDev.Save()
? oDev.cErrorMsg
*** Create a new entry
? oDev.New(), "TEst"
loData = oDev.oData
loData.Company = "TEST COMPANY"
loData.Name = "Rick Sttrahl"
? oDev.Save()
? oDev.Execute("select * from " + oDev.cFileName )
BROWSE
Ok, so this works just fine here, let's use the business object in a Fox form.
We'll build the following form based applet using the wwBusiness object with data that is retrieved over the Web. Note, that you can also switch operation of the application easily to pull data either from VFP or SQL Server tables locally simply by changing the properties on the instance of the class on the form.
This form uses a wwBusiness object instance to retrieve all data from a remote data source over the Web. This form contains very little code and requires no changes to pull data from local VFP or SQL data, or by pulling data over down over the Web; voila, the power of business objects!
LPARAMETERS lnListValue
IF EMPTY(lnListValue)
lnListValue = 1
ENDIF
THISFORM.Showstatus("Loading Developer List...")
loDev = THIS.oDeveloper
loDev.Query("select company,pk from wwDevRegistry ORDER BY Company","TDevelopers")
IF loDev.lError
MESSAGEBOX("Can't load customer data" + CHR(13) + ;
loDev.cErrorMsg,48,"wwBusiness Web Data Sample")
RETURN .F.
ENDIF
THISFORM.oList.RowSourceType= 2
THISFORM.oList.RowSource = "TDevelopers.Company"
THISFORM.oList.Value = lnListValue
THISFORM.ShowStatus("",RECCOUNT("TDevelopers"))THis is the most code we're going to write for this application in a single method!
ShowStatus simply updates the two panels of the statusbar:
LPARAMETERS lcPanelText, lnRecordCount IF EMPTY(lcPanelText) lcPanelText = "Ready" ENDIF THISFORM.oStatus.Panels(1).Text = lcPanelText IF !EMPTY(lnRecordCount) THISFORM.oStatus.Panels(2).Text = TRANSFORM(lnRecordCount) + " records " ENDIF
IF TDevelopers.Pk < 1 RETURN ENDIF THISFORM.LoadDeveloper(TDevelopers.pk)
and then implement LoadDeveloper like this:
*** LoadDeveloper()
LPARAMETERS lnPK
THIS.ShowStatus("Loading Developer...")
loDev = THIS.oDeveloper
IF !loDev.Load(lnPK)
MESSAGEBOX("Couldn't load this developer",48)
RETURN .F.
ENDIF
THISFORM.Refresh()
THIS.ShowStatus()
RETURN .T.*** New THISFORM.oDeveloper.New() THISFORM.Refresh()
*** Save
THIS.ShowStatus("Saving...")
IF !THISFORM.oDeveloper.Save()
MESSAGEBOX("Unable to save the developer entry." + CHR(13) + CHR(13) + ;
THISFORM.oDeveloper.cErrormsg,48)
THIS.ShowStatus()
RETURN
ENDIF
THIS.ShowStatus("Developer Entry Saved...") && Update the list by requerying
*** Delete
IF THISFORM.oDeveloper.Delete()
WAIT WINDOW NOWAIT "Developer entry deleted..."
THISFORM.LoadDeveloperList(THISFORM.oList.Value)
WAIT CLEAR
ELSE
MESSAGEBOX("Couldn't delete developer entry" + CHR(13) +;
THISFORM.oDeveloper.cErrorMsg,48)
ENDIF
Now point each of the buttons at these methods of the form: THISFORM.New(), THISFORM.Save(), THISFORM.Delete().
loPage = THIS.Parent
lcCompany = TRIM(loPage.txtCompany.Value)
lcState = UPPER(TRIM(loPage.txtstate.Value))
lcZIp = TRIM(loPage.txtZip.value)
lcCountry = TRIM(loPage.txtCountry.value)
THISFORM.oDeveloper.cSQLCursor = "TDevelopers"
lnResult = THISFORM.oDeveloper.Developerlistquery(lcCompany,,,lcState,lcZip,"",lcCountry,0,0,0,"Company","Company,pk")
IF THISFORM.oDeveloper.lError
MESSAGEBOX("Query failed" +CHR(13) + CHR(13) + ;
THISFORM.oDeveloper.cErrorMsg)
RETURN
ENDIF
IF lnResult < 1
MESSAGEBOX("No matches found...",64)
THISFORM.ShowStatus()
RETURN
ENDIF
THISFORM.oList.Requery()
Again we're reusing the business object method DeveloperlistQuery() reusing existing functionality to reduce the amount of code we have to write. This code simply queries the data again and returns a new resultset which gets re-bound to the listbox. The Search All button on the other hand simply calls the LoadDeveloperList() method to refresh the listbox with all entries from the server.
That's pretty much it! Note that when you save validation occurs if you don't fill in the form completely or make the service description too short. All the rules of the business object work just as they did before except we are now pulling the data from the Web!
If you wanted to pull the data from your local SQL Server instead, change nDataMode to 2 and add a cSQLConnectString for the connection and off you'd go against SQL Server. Change the nDataMode to 0 and set the cDataPath appropriately and off you go against Fox data!
I hope this example has demonstrated the power and flexibilty you have with this simple business object.