The typical example for this case is the previously discussed 1:n relation order – order details. After each record with order data, the order details for that data shall be printed.
The printing of a data line is triggered by LlPrintFields(). Analogously to the behavior of LlPrintFieldsEnd() in the last section, the function returns LL_WRN_TABLECHANGE if the user has placed a sub-table, and you then have to respond.
You can ask for the table relation with LlPrintDbGetCurrentRelation() and for the name of the child table with LlPrintDbGetCurrentTableName(). With this information, you can invoke the auxiliary function PrintTable() from the last section again. This call must be placed directly after LlPrintFields() – thus from the function PrintTable() itself. The function must be changed in order to call itself recursively:
function
PrintTable(DataTable data object)
{
// DataTable is an adequate object for data access, e.g.
a
// table of a database, a class array
or similar
<repeat>
{
<define fields of
DataTable>
(LlDefineField,
LlDefineFieldExt,
LlDefineFieldExtHandle)
<print
row>
(LlPrintFields)
<as long as warning
repeat>
(LlPrint,
Ret = LlPrintFields)
<as long as Ret =
LL_WRN_TABLECHANGE repeat>
{
<get current table name>
(LlPrintDbGetCurrentTable)
<get current
relation>
(LlPrintDbGetCurrentTableRelation)
<get current sorting>
(LlPrintDbGetCurrentTableSortOrder)
<generate an appropriate DataTable child
object>
<Ret = PrintTable(child DataTable)>
}
<next record in
DataTable>
}
<until last record in DataTable
is reached>
<print footer
line>
(Ret =
LlPrintFieldsEnd)
< as long as
warning "page full" repeat >
(Ret =
LlPrintFieldsEnd)
<result =
Ret>
}
Any sequence of tables and sub-tables can be printed with this code. The recursion ensures that it works properly with any "depth", i.e. this code can control arbitrary multilevel relations.