Using DynaReporter Grouping with a Custom Presentation
WebObjects includes template driven dynamic page delivery and elements like WORepetition. All of these things make it useful for creating reports. However, what WO doesn't give you out of the box is a mechanism for grouping objects you fetch into sub-groups. Often you don't want to list just the objects you fetched; you want to cluster the items fetched into sub-lists for display or processing. DRGrouping provides a complete kit of objects that can split appart a list of objects into groups based on complex rules (or master criteria) that you define. These rules can be expressed as arrays of objects called DRMasterCriteria. Furthermore, these objects can be persisted in a human-readable plist format.
The WRReporting.framework is built on top of the grouping framework, DRGrouping.framework. DRGrouping has no WOF dependencies so you can use it in any kind of app regardless of UI.
Using DRGrouping Programmatically
If you have an array of EOs, GenericRecords, or dictionaries, you can group them by attribute using DRGrouping.framework. This line creates a new DRReportModel from your list of objects (objs) and a array of master criteria (mcrits) to group by:
mod = new DRReportModel(objs, mcrits, null);
You might have got your objects from a dataSource, a displayGroup, a editingContext, where-ever. The master criteria list, mcrits will typically just have only one member since usually one wishes to group a result set by one paramter. As a convenience there is a class method on DRReportModel: public NSArray masterCriteriaForKey(String key) that creates an array filled with one DRMasterCriteria which in turn is configured to group each record by the equality of the value of a single attribute named by 'key'.
To see the effect of this form of grouping creation in action, change the attribute key name in the textbox labeled 'Group By:' Originally this value is set to 'category' so you see the Movies below grouped under Movie categories like 'Action', 'Horror', and 'Drama' among others.
The following code shows how to do the same thing as masterCriteriaForKey programmatically:
public NSArray masterCriteriaForKey(String key){
NSMutableArray mcrits = new NSMutableArray();
NSMutableArray smcs = new NSMutableArray();
DRSubMasterCriteria smc =
new DRSubMasterCriteria(key, false, false, null, null, false, null);
smcs.addObject(smc);
DRMasterCriteria mc = new DRMasterCriteria(smcs, null);
mcrits.addObject(mc);
return mcrits;
}DRSubMasterCriteria is the actual workhorse of a DRMasterCriteria. It embobies most of the rules. You might have more than one DRSubMasterCriteria if you wanted to group by two criteria at once (like a compound primary key). Note that DRSubMasterCriteria has many other options beside the attribute involved (7 in all). But for the most common kind of grouping, all you need do is pass in the first argument, the 'key' (attribute name you want to group by). All the other arguments are typically either false or null. You might use the WRReportEditor.wo on your pages as a means to edit all criteria parameters graphically. In the app below you can experiment with the rules on DRMasterCriteria and its DRSubMasterCriteria using this component. Click the triangle by 'Explode Editor' below to show the Editor. It will be empty at first. Clicke the Add button and you'll see a new DRMasterCriteria configured with one DRSubMasterCriteria. You can see the effect of your model on the report by clicking the button labeled 'Update Grouping Criteria'. Notice that after you do this, you override the setting in the textbox labeled 'Group By'.
To get the groups of records created by the DRReportModel's grouping engine, you can ask your DRReportModel instance a few questions:
NSArray grps = mod.groups();
DRGroup grp = grps.objectAtIndex(0);
NSArray recGrpList = grp.recordGroupList();
At this point you have an array of DRRecordGroup objects, 'recGrpList'. A DRRecordGroup is composed of two interesting items: the criteria value (encapsulated in a DRCriteria object) being grouped for (e.g. 'Action', 'Horror', or 'Drama' if we were grouping by 'category'), and the list of records matching that DRCriteria. You can get the DRCriteria for a DRRecordGroup by sending it a 'criteria()' message. You can get the list of records (usually EnterprisObjects) by sending the message 'rawRecordList()' to the DRRecordGroup.
If you want the name of the DRCriteria for each record group, you can simply send it a 'label()' message.
Details from the Use of DRGrouping API in the Example Below
The custom report in the page below is constructed with a WORepetition nested inside a WORepetition. The top-level repetition is configured with these bindings:
rep2: WORepetition { item = aDRRecordGroup; list = recordGroups; }The inner WORepetition's list is populated by asking each 'aDRRecordGroup' for its rawRecordList like so:
Repetition3: WORepetition { item = aMovie; list = aDRRecordGroup.rawRecordList; }Since its possible on this page (using the WRReportEditor) to add more than one DRMasterCriteria, the method on the page feeding the list of the top-level WORepetition has to check that there is not zero resulting DRGroups. If there are zero , then a an empty array is returned. If there are more than one DRGroups, only the recordGroupList for the first one is used. A DRReportModel has one DRGroup for each DRMasterCriteria. The DRGroup represents a dimension. The page below though is only equipped to handle one dimension of reporting. To get the list of DRRecordsGroups for a given DRGroup, you can send the DRGroup a 'recordGroupList()' message. To get the list of DRGroups from the DRReportModel, you can send the DRReportModel a 'groups()' method. The code below is the 'recordGroups()' method on the page below responsible for gathering this info from the DRReportModel and returning the top-level list of DRRecordGroups:
public NSArray recordGroups() {
Session s = (Session)session();
NSArray recGrps;
NSArray grps = s.reportModel.groups();
if(grps.count() > 0){
DRGroup grp = (DRGroup)grps.objectAtIndex(0);
recGrps = grp.recordGroupList();
}else{
recGrps = new NSArray();
}
return recGrps;
}
Note, DRRecordGroup also responds to 'recordList()'. This method returns a list of DRRecords objects instead of 'raw' records. A DRRecord wraps exactly one 'raw record'. A raw record is one element of whatever array of objects you originally grouped in the first place, such as an EnterpriseObject. DRRecords are useful if you are using the DRReportModel's totalling and attribute clustering features. Use of those features is beyond the scope of this readme though. You might want to look at the WRReporting.framework's WRReport class for an example of the usage of all the features available for reporting in the DRGrouping.framework.