Calculated Fields
The CalculatedFields desktop sample demonstrates how to add calculated and lookup fields to a DADataTable. A "calculated" field derives its value based on a calculation using values from other fields, where as a "lookup" field uses a key to lookup and retrieve the required value from another table.
Getting Started
The Desktop sample is typically located in /Developer/RemObjects Software/Samples/Data Abstract/Desktop/CalculatedFields
, though you may have installed the Data Abstract for Cocoa and it's samples in another location.
To build it, you will of course need Xcode, and like all the samples provided with Data Abstract for Cocoa you will need to be running the Relativity Server with the DASamples
Domain and the Simple
Schema available.
Running the Sample
This sample simply displays the results of adding 3 calculated fields and one lookup field.
When this sample is run you will see an empty table and a toolbar which contains a button and a pop-up. The button "Load Orders" will retrieve the required tables and then add the calculated and lookup fields before displaying the results in the table. The "Servers" popup contains a list of available servers discovered by the Zeroconf discovery system. When the sample is run, it starts a Zeroconf discovery service that looks for instances of Relativity Server running on the local network. Any discovered servers will be added to the popup, if no servers are discovered then the dropbox on the toolbar will be empty. You can specify a server address yourself by clicking on the popup and then clicking on "Specify custom URL...".
Examining the Code
The code for creating the connection to the server and handling logging in is covered by other samples. In this section we shall focus solely on how you add calculated and lookup fields.
NOTE If the table name, or the field names do not exist then an ROException
will be raised and should be handled appropriately.
App Structure
The sample is built around four classes; AppDelegate
, DataAccess
, ServiceAccess
, and RegisterServiceWindowController
.
The DataAccess
class handles everything related to interacting with the Data Abstract SDK; including retrieving data from an instance of Relativity Server and applying changes back to the server. Here beginGetData
retrieves all of the tables required (Providers
, Clients
, OrderType
, Orders
and OrderDetails
) using the methods getReferences
and getOrders
, then setupData
makes use of those tables to add the calculated and lookup fields; the code below comes from this class.
The ServiceAccess
class handles the discovery of any instances of Relativity Server that are available on the local network using ROZeroConf which is a feature available with the Remoting SDK that Data Abstract is built upon. ServiceAccess
sets up a ROZeroConfBrowser Class object which searches for any servers that broadcast a value matching the value defined for RELATIVITY_SERVICE_NAME
. When a service is found, or indeed disappears, the server list is updated and the popup with the list of available servers is also updated. It also handles the registration of custom server addresses. To explore further the ServiceAccess
class and Zeroconf discovery see the article: The ServiceAccess Class and Zeroconf discovery.
The RegisterServiceWindowController
class is a subclass of NSWindowController
which handles the UI aspects of a user manually adding a server url.
Lastly the AppDelegate
class handles the primary setup of the application, registers that it will listen for notifications broadcast by the ServiceAccess
and DataAccess
classes, and acts as a delegate to the RegisterServiceWindowController
, DataAccess
and NSTableView
classes. The main thing of note is that the dataIsReady
method is called when the NOTIFICATION_DATA_READY
notification is received, which causes the table to update the table with the newly received data.
Adding a Calculated Field
To add a calculated field to a DADataTable you need to either use the addCalculatedFieldName:dataType:target:selector: method of DADataTable, or manually create an instance of DACalculatedFieldDefinition and add it using the addCalculatedField: method.
Here we use the addCalculatedFieldName:dataType:target:selector: method which takes 4 arguments:
- Name - which is the unique name to be given to the field/column
- dataType is the data type for the field which should be one of the types defined in DADataType
- target is the object that will provide the method used for calculating the value of this field
- selector is the method/selector on the target that will be called to calculate the fields value.
In the sample below, a new calculated field is added to the OrderDetails
table, with the name "Sum", which will use the "datCurrency" data type which is used for monetary values, the target is "self" (the DataAccess
class) and the calculateSumForDetailRow:
is the selector that will be called. The calculateSumForDetailRow:
method takes the value from the "Amount" field and multiplies it by the value in the "Price" field and returns that result.
//DataAccess.m
- (void)setupData {
[self.orderDetailsTable addCalculatedFieldName:@"Sum"
dataType:datCurrency
target:self
selector:@selector(calculateSumForDetailRow:)];
//...
}
-(id)calculateSumForDetailRow:(DADataTableRow *)row {
NSInteger amount = [row[@"Amount"] integerValue];
double price = [row[@"Price"] doubleValue];
double sum = amount * price;
return [NSNumber numberWithDouble:sum];
}
Adding a Lookup Field
An alternative to the calculated field, is the lookup field which takes its value from a different table than the source table. There are two ways to add a lookup field, the first is by using the addLookupFieldName:sourceField:lookupTable:lookupKeyField:lookupResultField: method of DADataTable, the other way is to manually define a DALookupFieldDefinition object and pass it to the table using the addLookupField: method.
Here the addLookupFieldName:sourceField:lookupTable:lookupKeyField:lookupResultField: method is used, which takes the following arguments:
- Name a unique name to be given to the lookup field.
- sourceField the field that will be used as the key value for matching against the
lookupKeyField
- lookupTable takes a reference to the lookup table
- lookupKeyField the key field in the
lookupTable
that will be used to match against thesourceField
- lookupResultField the field in the lookup table that will be used as the result when the
sourceField
andlookupKeyField
match
Here we are looking up the textual name for an order type, a new field is added with the name TypeName
. The Orders
table has a Type
field which contains a numeric value which acts as a key against the Id
field of the OrderType
table, when a match is found the value in the Name
field of the appropriate row is returned.
//DataAccess.m
- (void)setupData {
//...
[self.ordersTable addLookupFieldName:@"TypeName"
sourceField:[self.ordersTable fieldByName:@"Type"]
lookupTable:self.orderTypeTable
lookupKeyField:[self.orderTypeTable fieldByName:@"Id"]
lookupResultField:[self.orderTypeTable fieldByName:@"Name"]];
}