Omnis Technical Note TNDM0001
Using Complex Data Structures
For Omnis Studio version 2.0
By Michael Monschau
Due to its publication date, this technote contains information which may no longer be accurate or applicable on one or more of the platforms it refers to. Please refer to the index page which may contain updated information.
How we can use row and list variables to design complex and flexible data structures.
Contents
Introduction
Defining row variables
Using the row variables
Introduction
When designing an Omnis file class we usually think in terms of flat data
and at the most complex level may have a list in our file class which
contains further rows of data. A typical example is an invoice file class.
Here we have some invoice details followed by a list of purchased items.
Simple Invoice
InvoiceNumber
InvoiceDate
InvoiceSubtotal
InvoiceDiscount
InvoiceTotal
InvoiceTax
SalesCode
SalesDepartment
CustomerCode
CustomerName
CustomerAddress
CustomerCity
CustomerStateProvince
CustomerZip
CustomerPhone
CustomerFax
InvoiceItems (list with columns)
Code
Description
Units
UnitPrice
Total
With the use of row variables we can structure the data much better.
Tidy Invoice
Invoice (row varible with columns)
Number
Date
Subtotal
Discount
Total
Tax
Sales (row varible with columns)
Code
Department
Customer (row varible with columns)
Code
Name
FullAddress (row varible with columns)
Address
City
StateProvince
Zip
Phone
Fax
Items (list with columns)
Code
Description
Units
UnitPrice
Total
By using row variables the overall appearance is very tidy. Our
file class would only contain four fields.
Defining row variables
Row variables are defined in the same way lists are defined. We could
have a code class method which defines our file class. We shall call it
$defineInvoice. We would call this method every time we need to define
our Invoice CRB, i.e. when we insert a new record.
Our $defineInvoice method would call other public methods to define the
row variables and lists of the Invoice CRB.
Our first listing is for the method $defineInvoice. This method calls
various other methods to define the row variables and list of our ÔfInvoicesÕ
file class.
##### Method '$defineInvoice' ##### | |
No. Method text | |
1 | Do method $defineInvoiceDetails (fInvoice.Details) |
2 | Do method $defineSalesDetails (fInvoice.Sales) |
3 | Do method $defineCustomerDetails (fInvoice.Customer) |
4 | Do method $defineInvoiceItems (fInvoice.Items) |
Next we have the $defineInvoiceDetails method. This method defines the
columns for our fInvoice.
Details field passed to the method in parameter one.
##### Method '$defineInvoiceDetails' ##### | ||||
No. | Parameter | Type | Subtype | Init.Val/Calc |
1 | pDetails | Field reference | ||
No. | Method text | |||
1 | Do pDetails.$cols.$add('Number',kInteger,kLongint) | |||
2 | Do pDetails.$cols.$add('Date',kDate,kDatetime) | |||
3 | Do pDetails.$cols.$add('Subtotal',kNumber,k2dp) | |||
4 | Do pDetails.$cols.$add('Discount',kNumber,k2dp) | |||
5 | Do pDetails.$cols.$add('Total',kNumber,k2dp) | |||
6 | Do pDetails.$cols.$add('Tax',kNumber,k2dp) | |||
Next we have the $defineSalesDetails method. This method defines the
columns for our fInvoice.
Sales field passed to the method in parameter one.
##### Method '$defineSalesDetails' ##### | ||||
No. | Parameter | Type | Subtype | Init.Val/Calc |
1 | pSales | Field reference | ||
No. | Method text | |||
1 | Do pSales.$cols.$add('Code',kCharacter,kSimplechar,10) | |||
2 | Do pSales.$cols.$add('Department',kCharacter,kSimplechar,10) | |||
Next we have the $defineCustomerDetails method. This method defines the columns for our fInvoice.Customer field passed to the method in parameter one. Additionally, customer details contains another row variable which we must define. In order to define row variables within row variables, we must first define a local row variable, and then calculate the row variable column 'pCustomer.FullAddress' from our local.
##### Method '$defineCustomerDetails' ##### | ||||
No. | Parameter | Type | Subtype | Init.Val/Calc |
1 | pCustomer | Field reference | ||
No. | Local Variable | Type | Subtype | Init.Val/Calc |
1 | FullAddress | Row | ||
No. | Method text | |||
1 | Do pCustomer.$cols.$add('Code',kCharacter,kSimplechar,10) | |||
2 | Do pCustomer.$cols.$add('Name',kCharacter,kSimplechar,100) | |||
3 | Do pCustomer.$cols.$add(FullAddress) | |||
4 | Do method $defineCustomerAddress (FullAddress) | |||
5 | Calculate pCustomer.FullAddress as FullAddress | |||
Next we have the $defineCustomerAddress method which is called by $defineCustomerDetails.
This method defines the columns for our FullAddress field passed to the
method in parameter one.
##### Method '$defineCustomerAddress' ##### | ||||
No. | Parameter | Type | Subtype | Init.Val/Calc |
1 | pAddress | Field reference | ||
No. | Method text | |||
1 | Do pAddress.$cols.$add('Address',kCharacter,kSimplechar,100) | |||
2 | Do pAddress.$cols.$add('City',kCharacter,kSimplechar,100) | |||
3 | Do pAddress.$cols.$add('StateProvince',kCharacter,kSimplechar,100) | |||
4 | Do pAddress.$cols.$add('Zip',kCharacter,kSimplechar,100) | |||
5 | Do pAddress.$cols.$add('Phone',kCharacter,kSimplechar,100) | |||
6 | Do pAddress.$cols.$add('Fax',kCharacter,kSimplechar,100) | |||
Finally we have the $defineInvoiceItems method. This method defines the columns for our fInvoice.Items field passed to the method in parameter one.
##### Method '$defineInvoiceItems' ##### | ||||
No. | Parameter | Type | Subtype | Init.Val/Calc |
1 | pItems | Field reference | ||
No. | Method text | |||
1 | Do pItems.$cols.$add('Code',kInteger,kLongint) | |||
2 | Do pItems.$cols.$add('Description',kCharacter,kSimplechar,100) | |||
3 | Do pItems.$cols.$add('Units',kInteger,kLongint) | |||
4 | Do pItems.$cols.$add('UnitPrice',kNumber,k2dp) | |||
5 | Do pItems.$cols.$add('Total',kNumber,k2dp) | |||
Implementing complex data structures like this one is a little more work then using a flat structure, but depending on what you are attempting to do, the benefits may outweigh the disadvantages.
Using the row variables is easy. To refer to a column we simply describe
the full path to the column. i.e. 'fInvoice.Customer.FullAddress.City'.
We can enter this in calculations, or as the $dataname property of data
bound window objects (see example library).
If we have other file classes which contain common blocks of data we may
reuse the functions to define row variables within those file classes.
Using row variables also makes it easier to transfer or copy blocks of
common data. We can easily calculate one row variable from another or
pass a block of data as a single parameter.
Additionally if we ever need to we can add columns to a row variable on
a need to need basis without having to do time consuming reorganizations
of lots of data. When a row variable in a record is being used we can
have a method which simply checks the column count and adds appropriate
columns to the row variable. Adding columns does not destroy the data.
##### Method '$extendCustomerDetails' ##### | ||||
No. | Parameter | Type | Subtype | Init.Val/Calc |
1 | pCustomer | Field reference | ||
No. | Method text | |||
1 | If pCustomer.$colcount<4 | |||
2 | Do pCustomer.$cols.$add('Credit',kNumber,k2dp) | |||
3 | End If | |||
However, one problem remains. If you want to create indexes of any of the fields stored in a row variable, you can't. One solution is to have a copy of the fields you which to index in the root of the file class. You can still keep the actual fields within the row variable to keep the data block complete. Of course you must remember to keep the copies up to data. The alternative is, any fields you wish to index you do not store in row variables.