Category Archives: West Wind

Table based definition of Parent-Child relationships in wwBusinessPro

The wwBusinessPro data access library supports table-based definitions of the Business Objects used in an application. This also allows you to define Parent-Child relationships between the records and tables in your app.

You can download wwBusinessPro from the project hosted on BitBucket.org.

The default table name for the Business Object definition table is “wwBusinessObjects”.dbf”. Just make sure the Path includes the location of this table, and run the form “wwDictionary.scx” to open and edit the rows in this maintenance form.

Here is a screenshot of the Business Object Dictionary form included in wwBusinessPro:

SNAGHTML201d0768

Creating a Parent Business Object with wwBusinessPro

When using a wwBusProParent, you set properties on the Parent that tell it about the Child class.
Then, the Parent will create the Child class for you.
You do not create the Child when writing your code, let the Parent do that.

You need to create a VCX library and put a Child class for each Child collection in there,
with its Child properties already set up. This allows the Parent to create the Child when he
needs it. If you follow the path of the Get() method, you will see that the Parent creates a
Child class object and attaches it to its oLineItems property. So, once your Child classes are
configured in a VCX, you will not actually have to write any code to access them (unless you are
doing some special case, custom coding), you just set up the Parent so he can find them.
The Parent will handle all that for you, including the fetching of the related Child records.

You can also put your Parent objects in the VCX and set their properties in the VCX file.
This sample code is shown here simply to show which properties are required to make it all work.


*=================================
* == Sample to  show a Parent BO configured to use a String lookup and work with related Child Records
*=================================

Local loSrHead AS ‘cSrHead’

Set ClassLib To MyMainLibrary Additive
Set ClassLib To MyBoLibrary Additive  && Include your Business Object ClassLibrary

loSrHead = CreateObject(‘MyInvoiceBoClass’) &&  A Parent class found in your BO Library VCX

*– These settings are native to the base wwBusiness operation
loSrHead.cFileName = ‘tblSrHead’ && The table name (.dbf file or Sql Server table name)
loSrHead.cAlias = ‘csrSrHead’  &&  Local alias name to use when opening file
loSrHead.nDataMode = 0  && Data access mode for the business object. 0 – Native Fox dbf, 2 – SQL Server
loSrHead.cPKFIeld= ‘PK’    && Points to the unique Key value column in the table. Usually an Integer column.

*– This code section is part of wwBusinessPro feature set wwBusinessPro allows for String-based lookups and related Child cursors…
loSrHead.cLookupField = ‘storeno+docno’  && This is an example of a compound string unique key lookup value.
loSrHead.cAdditionalFilter = ‘mtype = [0]’  && (Optional) Provides a way to further filter the record locating process.
loSrHead.cPKFIeld= ‘storeno+docno’           && Set to same value as cLookupField if no Integer PK is available in the table.

loSrHead.lLoadLineItems = .t. && Tells it load Child records when getting the Parent record.
loSrHead.lAllowEditLineItems = .t.  && Tells it create a ReadWrite cursor when creating the Child record cursor

*– Setup the Child properties on the Parent
loSrHead.cLineItemsClass = ‘cSrItems’  && Child class must exist in the MyBoLibrary.vcx
loSrHead.cLineItemsAlias = ‘curSrItems’ && Cursor name where you want the Child records to be stored

*– Fetch the Parent and create a Child Cursor
loSrHead.Get(‘TWU STORE RN2009100003’) && For String lookups
loSrHead.Get(12345) && For Integer lookups

If loSrHead.lFound = .f.
*– Add any Not Found handling code here
Endif

If loSrHead.nErrorCount > 0
*– Add any Error handling code here, if not already handled in your classes.
Endif

*– Make some changes to the loSeHead.oData object and to the Child cursor
*– and save the changes…
loSrHead.Save(.t.) && .t. Tells it to save changes to the Child records also

 

One more thing about the Child classes…

When setting up your Child in the VCX, these are the properties you need to set on it:

cFilename (Required. Name of the dbf or Sql Server table)
cAlias (Optional. Will use cFilename by default)
cPKField (Must be an Integer. See “Important Note” below)
cFKField (Must be an Integer. Maps to Parents PK field)
cLookupField (Optional. Maps to Parents cLookupField. Use when Parent uses for String lookups)
cOrderFld (Optional. Can be used to set the ordering of child records. i.e. a sequnce field)

These properties provide the info needed by the LoadLineItems() method work out of the box.
Sometimes you may want to overwrite the LoadLineItems() method with custom code, but the default
should work in basic configurations.

IMPORTANT NOTE:
In order to save changes on the Child records, they need a unique PK that can be used. See cPKField.
String cLookupField works fine to load the records in, but it does not guarantee uniqueness when
trying to save changes back.

Again, you can use cLookupField Stings for loading the records, but you must have Integer PKs
on your Child records to allow saving changes back.

wwBusinessPro–Parent Child collections in a wwBusiness business object

Note: This post if part of a complete series on the West Wind wwBusiness library.
Click here to see the full index

 

Before we start digging into parent/child data access with wwBusinessPro, let’s have a review of the basics. Suppose we want a Invoice object, and child set of Invoice Line Items that belong to that invoice. This is possible, even simple, with wwBusninessPro. In fact, in this post I’ll also show you how to handle a second child collection, like Invoice Payments, related to the Invoice. If you follow the example shown here, you’ll be able to handle cases where you have a single business objust with MULTIPLE child collections.

For example, here is what a really complex “aggregate” business object can look like. In my main business app, I have one complex business object that loads 4 additional child cursors in addition to the base oLineItems collection, and, it also loads customer and user info into oCustomer and oUser objects that I added onto the BO.

clip_image006

So let’s get started…

Parent and One Child Cursor

Parent class – Create your Invoice class as subclass of wwBusProParent.

At this point, you will see that it has *ONE* child collection object and related properties already built in. The oLineItems object will hold a object reference to the child item class. To make it active (so that it fetches child records every time you fetch a new parent object), you will need to set the cLineItemsClass, cLineItemsAlias, and lLoadtems properties.

The cLineItemsClassproperty points to a lineitems class that you must define in your VCX and it holds the table name of where the child records live and the key field name which links it to the PK on the parent.

The cLineItemsAliasproperty is the name of the cursor that where the child records will live.

Finally, be sure you set the lLoadLineItems property to .t. to make sure the main BO will load the line items each time you call the Get() method on the Parent.

Child Class – Create your InvoiceLineItems child class as a subclass of wwBusProItemList.

Set the properties cFilename and cFKField, which identify the Child table, and the field in the table that links back to the Parent PK in the invoice class.

So that’s it.  You’ve just set up the built-in functionality for the wwBusinessProParent class to handle the Parent BO and its set of child objects.

 

Need to handle MULTIPLE sets of child records?

Let’s say you also want to fetch all InvoicePayments records every time you load up an Invoice object…

After you’ve configured your “Invoice” and “InvoiceLineItems” classes as described above, and add the following properties and one method to cover the “Payments” collection:

New Properties:

  • cPaymentItemsClass
  • cPaymentItemsAlias
  • lLoadPayments
  • lAllowEditPaymentItems (optional, but something I include on my so I can enable or disable stuff in the UI layer)

New Method:

  • GetPaymentItems()

Now you need write two simple code blocks:

1. To populate the PaymentItems cursor (in addition to the LineItems cursor) every time you fetch an Invoice, you need to override the LoadRelatedData() method on the Invoice class like this:

 image

 

2. Define the GetPaymentItems() method like this:

image

That’s it.

Once you have brought this Invoice object to life, you will have 3 classes in your VCX:

Invoice (based on wwBusProParent)

InvoiceLineItems (based on wwBusProItemList) (this is what the default oLineItems will point to)

InvoicePaymentItems (based on wwBusProItemList) (this is what the above new properties and methods will point to)

When complete, your “Aggregate” Invoice Business Object will look like this:

Invoice

.oLineItems

.oPaymentItems

I know it might seem complicated at first, but once you understand what’s happening, it really not hard.

wwBusinessPro – Article 2: Configuring Child classes (wwBusProItemList)

Note: This post if part of a complete series on the West Wind wwBusiness library.
Click here to see the full index

Another enhancement I added to wwBusinessPro was to allow for Child record collections to be easily used with a Parent BO.

When using a wwBusProParent, you set a few properties on the Parent that tell it about the Child class. Then, the Parent will create the Child class for you. So, when you’re working with your BO’s in code (after everything is all configured), you do not create the Child, you let the Parent do that. You will create a VCX library and add a Child class for each Child collection in there (based on wwBusProItemList), with its Child properties already set up. This allows the Parent to create the Child when he needs it. So you can see how this works, if you were to follow the path of the Get() method (remember, this is a key extension method I added in wwBusProParent), you will see that the Parent creates the Child object and attaches it to its oLineItems property. So, once your Child classes are configured in a VCX, you will not actually have to write any code to access them (unless you are doing some special case, custom coding), you just set up the Parent so he can find them. The Parent will handle all that for you, including the fetching of the related Child records.

Code Sample: Creating and using your first Parent Business Object

Parent Setup:

When using a wwBusProParent, you set properties on the Parent that tell it about the Child class.
Then, the Parent will create the Child class for you.
You do not create the Child when writing your code, let the Parent do that.

You need to create a VCX library and put a Child class for each Child collection in there,
with its Child properties already set up. This allows the Parent to create the Child when he
needs it. If you follow the path of the Get() method, you will see that the Parent creates a
Child class object and attaches it to its oLineItems property. So, once your Child classes are
configured in a VCX, you will not actually have to write any code to access them (unless you are
doing some special case, custom coding), you just set up the Parent so he can find them.
The Parent will handle all that for you, including the fetching of the related Child records.

You can also put your Parent objects in the VCX and set their properties in the VCX file.
This sample code is shown here simply to show which properties are required to make it all work.

Child Setup

When setting up your Child in the VCX, these are the properties you need to set on it:

cFilename (Required. Name of the dbf or Sql Server table)
cAlias (Optional. Will use cFilename by default)
cPKField (Must be an Integer. See “Important Note” below)
cFKField (Must be an Integer. Maps to Parents PK field)
cLookupField (Optional. Maps to Parents cLookupField. Use when Parent uses for String lookups)
cOrderFld (Optional. Can be used to set the ordering of child records. i.e. a sequnce field)

These properties provide the info needed by the LoadLineItems() method work out of the box.
Sometimes you may want to overwrite the LoadLineItems() method with custom code, but the default
should work in basic configurations.

IMPORTANT NOTE:
In order to save changes on the Child records, they need a unique PK that can be used. See cPKField.
String cLookupField works fine to load the records in, but it does not guarantee uniqueness when
trying to save changes back.

Again, you can use cLookupField Stings for loading the records, but you must have Integer PKs
on your Child records to allow saving changes back.

wwBusinessPro – Article 1: String Lookups

Note: This post if part of a complete series on the West Wind wwBusiness library.
 

I’ve written an extension library on top of the West Wind wwBusiness classes. You can read the original post on my work here.  In my enhanced library, which I’ve named wwBusinessPro!, one of the things I’ve added is a lookup method that will handle String ID lookup values in addition to the native handling of Integer PK’s. The base wwBusiness library only works with Integer PK lookups, and I needed an enhancement to work with String lookups to get started with wwBusiness in my app.

When using my wwBusinessPro class, the main method that you call to do a lookup is the Get() method. You will call this method even when doing a Integer PK lookup because it will expose all the other extensions I’ve added to the library (more on that later).

So, the Get() method accepts one parameter which can be an Integer or a String.

    oJob.Get(123) && An Integer PK lookup

     – Or –

    oJob.Get(‘123’) && A String lookup

Now, on each Business Object class that you create, you need to specify the following properties to allow any kind of lookup to work. (This is how the base wwBusiness functions, so this is not a new feature in wwBusinessPro!, except that I have extended it to work with String values too). The cPKField setting is required for all cases, and cLookupField is only required for String-based key values, in which case you must specify the same column names for both properties.

cPKField (This property is required. Choose either and Integer Pk column or a String key value column)

For Integer PK’s set this value to the name of the Integer PK column in the table. (basic wwBusiness feature)

For String PK’s set this value to the name of the String key value column in the table. (new feature)

Note: this key value is what will be used to locate your record when saving changes to existing records. For String key values, be sure you do not use Set Exact Off or else you may update the wrong record based on a partial string match.

cLookupField (optional)

Leave blank if you do not have a String value lookup field. (This is an extension property that I added to handle String lookups.)

For example, in an oJob class, you would set the cLookupField to the name of the column that has the string lookup key in it. For my Job table, the cLookupField property is set to ‘job_num’ which is a string column. Now it knows to look for the string value ‘123’ in the ‘job_num’ column. I also have Integer PK’s on that same table, so I also set the cPKField property to ‘ipkey’. Now my processing can also be done by the PK Integer value, if I know it. So I can now do lookups either by Strings or Integer PKs.

How it works:

The Get() method will determine what kind of lookup you are trying to do based on the data type of the value you pass in (integer or string) and will make the correct method call to other methods to do the actual lookup, all of which eventually drill down into the base wwBusiness class to do the lookup. Just pass in a string or an integer and it will happen like magic. You can then check the oJob.lFound property to determine if the requested record was found.

Here’s the code for the Get() method:

image

 

More about string lookups:

For string lookups, if you need more flexibility on the cLookupField, you can bypass the Get() method and use a lower-level lookup that accepts an alternate lookup column. It’s called LoadByRef() and accepts a string lookup value and a column name in which to perform the lookup. Like this:

oJob.LoadByRef(‘53443’, ‘po_num’) && Load a job by the po number.

And, to take it one step further, there is one more way you can further control the lookup, and that is by setting a property called cAdditionalFilter. Rather than accepting it as a parameter into the LoadByRef() method, it works as a property that you set before calling the method. This powerful feature allows, for example, find the first record with Status=”A” for Customer “DEN001”. You will just have to be creative and explore all the ways this method can be used. For DBF mode, the base wwBusiness method uses the LOCATE command, so the lookup is from the top down, so it will find the first record. For Sql Server mode, you can even include an Order By clause at the end of the cAddtionalFilter to all effectively working from the bottom up.

Example:

    oJob.cAdditonalFilter = ‘Status = [A]’
    oJob.LoadByRef(‘DEN001’, ‘cust_num’)

You can use any complex expression you want to in the cAdditionalFilter property:

    oJob.cAdditonalFilter = ‘Status = [A] and Amount>1000’

Remember, in my methods, I am still eventually using the base wwBusiness methods to do the work, it’s just that I have built a wrapper around it to make it easier to use.

Here’s the method code for LoadByRef()

image

 

 

Note: This post if part of a complete series on the West Wind wwBusiness library.

Exploring my wwBusiness extension library

Updated 02/01/2010 1:26 pm

I’ve written an extension library on top of the West Wind wwBusiness classes for object oriented data access in Visual FoxPro, which I’ve named wwBusinessPro. In this blog series I will explain some of the basics about the base West Wind wwBusiness data access library and my extension library wwBusinessPro, which is built on top of it.

You can read the wiki pages  and download the wwBusinessPro library from the BitBucket repository.

I’ve prepared several articles explaining some basics of the main wwBusiness library from West-Wind Technologies and how my wwBusinessPro extension library adds support for String lookups and Parent-Child data models for your applications.

It’s not hard to get started… I recommend this strategy:

1. Before you try to jump in, take time to read each article listed below.

2. When you are ready to start, follow the instructions in the post Setting up wwBusinessPro.vcx to work with wwBusiness.vcx

3. Now create a new class library for your Business Objects and add a Parent BO class inherited from wwBusProParent, and one or more Child BO classes (ones that are related to the Parent you created) which inherit from the wwBusProItemList class.

4. Next, add data rows to the wwBusinessObjects.dbf table to setup your Parent and Child business objects. (see wiki page for screenshot). There will be one row for the Parent, and a row for each Child that relates to the Parent.

Now you’re ready to go… With a Parent and a Child BO properly configured, you can actually drive the whole thing from the Command Window, which is a good way to play around with it and see it work. See this code sample for a typical code snippet that is used to work with BO’s. After a little testing you’ll be ready to create (or modify) your forms to use the Parent/Child BO pair. It’s not all that hard, you just need a simple get-started case to help you see it all come together.

Articles

Intro and overview articles:

Read my original review of the West Wind wwBusiness library here 

Article 1 – String Lookup enhancements in wwBusinessPro

Article 2 – Parent-Child enhancements in wwBusinessPro

 

Specific Steps to use wwBusinessPro BOs in your code and forms:

Step 1 – Setting up wwBusinessPro.vcx to work with wwBusiness.vcx

Step 2 – Creating and using your first Parent Business Object (wwBusProParent class)

            (also see this Help entry from the base wwBusniess Help File)

Step 3 – Setting up Child cursors with Parent BOs

           (also see this Help page from the base wwBusiness Help File)

 

Other wwBusiness resources:

Main wwBusiness Help File from West Wind (click Class Reference –> Class wwBusiness)

Article by Kevin Cully about wwBusiness (PDF)

 

Licensing:

wwBusinessPro is a free open-source project, but you’ll need to purchase your own license for the main wwBusiness library from West Wind.  It is included as part of the West Wind Web Connection package and  also included in the West Wind Client Tools package at the West Wind web site.

 

If you have any questions or need specific help please comment below.

West Wind Client Tools for VFP

Note: This post if part of a complete series on the West Wind wwBusiness library.


Introducing wwBusiness

An object oriented data access library for Visual FoxPro

I recently got interested in converting my company’s main Visual Foxpro CRUD application to a Business Object model, rather than old-school free .dbf tables with a bunch of procedural code stuck in all the button clicks and textbox valid() events. I’m sure none of you have ever done such hack-work, but back in the beginning (I started with FPD in 1992, then FPW, then VFP as each was released) that’s how I started. I was a solo developer and had no formal training or access to mentors. It took me a long time to get the vision of how real programming should be done, but I finally learned some of these core fundamentals a few years back, and I’m really enjoying the process of refactoring my app into this BO pattern. I began by reading about the topic in all the usual places, and posted a few questions here and there on several VFP sites (like http://www.foxite.com/ and http://www.universalthread.com/), then I finally hacked out my own basic BO class, after I learned about the SCATTER approach that is widely used to capture data from a table row into an object.

Then, at a pair programming session with a Foxpro developer friend of mine, he showed me Rick Strahl’s West Wind Web Connection for VFP that he’s used for years. Turns out that package has a nice BO class library, called wwBusiness, which is also available as part of a client app tool set from West Wind (called the West Wind Client Tools for VFP http://www.west-wind.com/wwClientTools.asp ). So the Web Connection package and the Client Tools package both have the same wwBusiness BO class library.

Now, the West Wind Client Tools package also has a TON of stuff besides just the BO classes (like SMTP, HTTP, FTP, SQL, XML, JSON, SOAP, blah, blah). You can read all about them at the Client Tools page link I listed above.

One cool thing about the wwBusiness BO class is that it has one single property (nDataMode) to switch between VFP dbf tables and SQL Server, so it’s very helpful if you want to migrate your application’s data to a SQL Server backend and flip the switch once you are ready to move away from dbf tables. And the cool thing is that your app will never know where the data comes from, it just uses the wwBusiness BO to request data, and it comes back as an object that you can work with, then you call the .Save() method on the BO object to write the changes back to the DBF or Sql Server. I tested it, and it actually does work very well.

The wwBusiness.vcx class library has two class types:

  • wwBusiness – a single BO class (for fetching a parent record or a single line item)
  • wwItemList – a class which will fetch and hold a set of records (like line items on a Sales Order)

Together, these classes can model just about any Parent-Child data set you might have in your app.

Introducing wwBusinessPro

I found the two base classes in wwBusiness be good starting points for basic Object Oriented data access, but I have written my own wrappers around them and extended their functionality greatly, and I’ve named my enhanced library “wwBusinessPro”.

Download

You can download wwBusinessPro from the project hosted on BitBucket.org.

Features:


The wwBusinessPro library extends the base features of wwBusiness by adding these new features:

Here are the primary two ways I extended the classes:

  1. The wwBusiness (single BO class) expects an integer PK to do lookups and fetches. I indeed have Integer PKs on my tables, but I sometimes also have a unique lookup string for each parent record, and I wanted the option of using that for fetching a Parent record (and related child records too). So, I created a Get() method which will accept either an Integer or a String. It then calls the native wwBusiness.Load(tnPk) for Integer PK lookups or it will call my extension method LoadByRef(tcLookupString) for string based lookups. The wwBusiness class already had cPKfield out of the box, and I added a cLookupField property to handle string-based lookups. I also enhanced the Save() method to apply the Parent Key to all the Child collection records so that the relational structure required for Parent-Child PK/FK would be automatically handled for me.
  2. Out of the box, the wwItemList class requires YOU to write a method to fetch the desired child records into a cursor, then it loads that data from your cursor into an array when you call the wwItemList.LoadFromCursor() method. Well, that array would work fine for procedural processing, but for my application I needed to host the child records in a grid, so I needed to service the child records from that local cursor (rather than working on the array items). I wrapped the wwItemList Save() method to get the local records back into the real table from which they came. For my app design, this cursor-based functionality was the biggest missing part from the basic toolkit. I had to write about 3 or 4 methods to handle this workflow in an abstract way so I could have a generic solution for all my child item collections. The main methods I wrote were LoadLineItems() and a wrapper around SaveToCursor() which was needed to handle records that had been deleted from the local working cursor.

You can see a code sample of working with my extended wwBusiness classes here: http://codepaste.net/aascee
Here is a screenshot of the object model that I created with my extended wwBusiniess and wwItemList to create a Job object with several associated child collections: Line Items, Material Items, and Labor Items that are all from child tables related to the Parent record via PK/FK relationship.

wwBusinessObjectModel

Lastly, note that using a BO tool in general not only brings you data handling in an object-based manner with support for various back end data stores, but that same Business Object now becomes *THE* place to put business logic for each of your business objects. Like adding a method for CloseSalesOrder() or AssignJobPriority() or SetDefaultPropertiesForNewJob(), or PrintShippingTicket() etc. With all this properly in place on your Business Objects, creating a UI form becomes a much simpler task of just calling methods on the Business Object. Using this technique, I was able to reduce the code in my CRUD forms by more than 50%!! And now, I have a bunch of cute BOs I can instantiate in code or even right in the command window to work on them without even having a UI screen. Not only that, the same BO can easily support a web-based UI to your same exact data and business logic. It is truly beautiful!

So, as you can see, I did a lot of work on the West Wind classes to prepare them for my app, but two things: 1. It was VERY fun to work all this out, and 2. It works like a charm, and I now have a completely BO-based CRUD app where all the Business logic is nicely tucked away in the BOs and not in my forms or form controls.

Also, FoxPro veteran Kevin Cully is a long-time West Wind user, and he made a presentation on the wwBusiness classes to the Atlanta FoxPro User Group (AFUG) several years ago and prepared a nice 20-page introductory paper here: http://cullytechnologies.com/presentations/wwbusiness/cully_technologies_wwbusiness_presentation.pdf

Man, I wish I had this thing 10+ years ago when as I first began the VFP version of my app. Since I didn’t have a clue what I was doing at the time I started, it wound up a little bit messy, until this came along. I began my own BO efforts around May 2009, and the I abandoned my version in favor of wwBusiness around August 2009.

So go check it out for yourself… You can read the help file online and the package is only $199, and has 10% during August 2009, so about $179 gets you on his customer list and into the surf over at West Wind. You can download a working version with the full source code right from the site. The site also has a reasonably active discussion forum, and Rick is generally very responsive himself.

Note: This post if part of a complete series on the West Wind wwBusiness library. Click here to see the full index.