Agora precisamos de alguns grupos!

Vamos supor que você precisa agrupar por cidade. E precisa de uma linha com o nome da cidade no cabeçalho e a soma de todos os valores relativos àquela cidade no rodapé. É fácil, vamos ver o arquivo sales4.xml:
<?xml version="1.0" encoding="ISO-8859-1" standalone="no"?>
<!DOCTYPE REPORT SYSTEM "PHPReport.dtd">
<REPORT>
   <TITLE>Sales Report</TITLE>
   <BACKGROUND_COLOR>#FFFFFF</BACKGROUND_COLOR>
   <CSS>phpreports.css</CSS>
   <PAGE BORDER="1" SIZE="25" CELLSPACING="0" CELLPADDING="5" WIDTH="500">
      <HEADER>
         <ROW>
            <COL COLSPAN="4" CELLCLASS="PAGE_LAYER" TEXTCLASS="BOLD">John Doe Enterprises</COL>
         </ROW>
         <ROW>
            <COL COLSPAN="4" CELLCLASS="PAGE_LAYER" TEXTCLASS="BOLD">Sales Report</COL>
         </ROW>
      </HEADER>		
      <FOOTER>
         <ROW>
            <COL ALIGN="RIGHT" COLSPAN="3" CELLCLASS="PAGE_LAYER">page total</COL>
            <COL ALIGN="LEFT" NUMBERFORMATEX="2" CELLCLASS="PAGE_LAYER" TEXTCLASS="BOLD" TYPE="EXPRESSION">$this->getSum("value")</COL>
         </ROW>
      </FOOTER>		
   </PAGE>
   <GROUPS>
      <GROUP NAME="citygroup" EXPRESSION="city">
         <HEADER>
            <ROW>
               <COL CELLCLASS="GROUP_LAYER">city:</COL>
               <COL CELLCLASS="GROUP_LAYER" TEXTCLASS="BOLD" TYPE="EXPRESSION" COLSPAN="3">$header->getValue("city")</COL>
            </ROW>
            <ROW>
               <COL CELLCLASS="GROUP_LAYER">id</COL>
               <COL CELLCLASS="GROUP_LAYER">name</COL>
               <COL CELLCLASS="GROUP_LAYER">product</COL>
               <COL CELLCLASS="GROUP_LAYER">$</COL>
            </ROW>
         </HEADER>
         <FOOTER>
            <ROW>
               <COL ALIGN="RIGHT" COLSPAN="3" CELLCLASS="GROUP_LAYER">total</COL>
               <COL ALIGN="LEFT" CELLCLASS="GROUP_LAYER" TEXTCLASS="BOLD" NUMBERFORMATEX="2" TYPE="EXPRESSION">$this->getSum("value")</COL>
            </ROW>
         </FOOTER>
         <FIELDS>
            <ROW>
               <COL TYPE="FIELD" CELLCLASS="GROUP_LAYER">id</COL>
               <COL TYPE="FIELD" CELLCLASS="GROUP_LAYER">name</COL>
               <COL TYPE="FIELD" CELLCLASS="GROUP_LAYER">product</COL>
               <COL TYPE="FIELD" CELLCLASS="GROUP_LAYER" NUMBERFORMATEX="2">value</COL>
            </ROW>
         </FIELDS>
      </GROUP>
   </GROUPS>
</REPORT>
E agora se parece com isso:

sales.xml result file

Agora estamos conversando! :-) Nosso primeiro grupo coloca toda a informação sobre o campo city em um bloco e criou um cabeçalho e um rodapé para isso, com o campo value (valor) nele. Por favor note que eu alterei o nome do grupo (<GROUP NAME="citygroup">) e usei uma expressão para fazer ele quebrar (obrigatória em grupos que quebram - <GROUP NAME="citygroup" EXPRESSION="city">).

A expressão que você usa no atributo do elemento do grupo é um dos campos do relatório. Quando ele muda, automaticamente o grupo vai imprimir seu rodapé (se ele tiver um). Aí você pode usar uma das funções de agrupamento para pegar seus resultados. A referência $this-> vai apontar para o grupo corrente.

Note que eu também usei um atributo NUMBERFORMATEX="2" para arredondar os valores dos campos para duas casas decimais.

"Ok, agora eu quero fazer um sub-grupo, para cada cliente dentro de cada cidade" ... sem problemas"
Aqui está o arquivo sales5.xml:
<?xml version="1.0" encoding="ISO-8859-1" standalone="no"?>
<!DOCTYPE REPORT SYSTEM "PHPReport.dtd">
<REPORT>
   <TITLE>Sales Report</TITLE>
   <BACKGROUND_COLOR>#FFFFFF</BACKGROUND_COLOR>
   <CSS>phpreports.css</CSS>
   <PAGE BORDER="1" SIZE="25" CELLSPACING="0" CELLPADDING="5" WIDTH="500">
      <HEADER>
         <ROW>
            <COL COLSPAN="4" CELLCLASS="PAGE_LAYER" TEXTCLASS="BOLD">John Doe Enterprises</COL>
         </ROW>
         <ROW>
            <COL COLSPAN="4" CELLCLASS="PAGE_LAYER" TEXTCLASS="BOLD">Sales Report</COL>
         </ROW>
      </HEADER>		
      <FOOTER>
         <ROW>
            <COL ALIGN="RIGHT" COLSPAN="3" CELLCLASS="PAGE_LAYER">page total</COL>
            <COL ALIGN="LEFT" NUMBERFORMATEX="2" CELLCLASS="PAGE_LAYER" TEXTCLASS="BOLD" TYPE="EXPRESSION">$this->getSum("value")</COL>
         </ROW>
      </FOOTER>		
   </PAGE>
   <GROUPS>
      <GROUP NAME="citygroup" EXPRESSION="city">
         <HEADER>
            <ROW>
               <COL CELLCLASS="GROUP_LAYER">city:</COL>
               <COL CELLCLASS="GROUP_LAYER" TEXTCLASS="BOLD" TYPE="EXPRESSION" COLSPAN="3">$header->getValue("city")</COL>
            </ROW>
            <ROW>
               <COL CELLCLASS="GROUP_LAYER">id</COL>
               <COL CELLCLASS="GROUP_LAYER">name</COL>
               <COL CELLCLASS="GROUP_LAYER">product</COL>
               <COL CELLCLASS="GROUP_LAYER">$</COL>
            </ROW>
         </HEADER>
         <FOOTER>
            <ROW>
               <COL ALIGN="RIGHT" COLSPAN="3" CELLCLASS="GROUP_LAYER">city total</COL>
               <COL ALIGN="LEFT" CELLCLASS="GROUP_LAYER" TEXTCLASS="BOLD" NUMBERFORMATEX="2" TYPE="EXPRESSION">$this->getSum("value")</COL>
            </ROW>
         </FOOTER>
         <GROUP NAME="customergroup" EXPRESSION="name">
            <FOOTER>
               <ROW>
                  <COL ALIGN="RIGHT" COLSPAN="3" CELLCLASS="GROUP_LAYER">customer total</COL>
                  <COL ALIGN="LEFT" CELLCLASS="GROUP_LAYER" TEXTCLASS="BOLD" NUMBERFORMATEX="2" TYPE="EXPRESSION">$this->getSum("value")</COL>
               </ROW>
            </FOOTER>
            <FIELDS>
               <ROW>
                  <COL TYPE="FIELD" CELLCLASS="GROUP_LAYER">id</COL>
                  <COL TYPE="FIELD" CELLCLASS="GROUP_LAYER">name</COL>
                  <COL TYPE="FIELD" CELLCLASS="GROUP_LAYER">product</COL>
                  <COL TYPE="FIELD" CELLCLASS="GROUP_LAYER" NUMBERFORMATEX="2">value</COL>
               </ROW>
            </FIELDS>
         </GROUP>
      </GROUP>
   </GROUPS>
</REPORT>
E agora se parece com isso:

sales.xml result file

Tudo que você precisa fazer é inserir um elemento GROUP dentro de outro elemento GROUP. Nesse caso temos dois grupos, citygroup e customergroup. O citygroup é o grupo principal, o grupo que tem "filhos" mas não "pais" (o customergroup é um "filho") e o customergroup é o grupo de detalhe, o grupo onde os valores vão aparecer. Dê uma olhada no arquivo DTD para ver como os grupos são organizados.

Então, acabou e ... ESPERE!!! Onde está o total do relatório???

Putz, esqueci dele, mas não se preocupe, é fácil também !!!

Deixe-me mencionar que os elementos do DOCUMENT, HEADER (cabeçalho do documento) e FOOTER (rodapé) são separados do resto do layout da página, quero dizer, como eles apenas recebem os valores depois que processam todas as páginas, são tabelas diferentes (lembre-se: a página inteira é uma tabela) depois da tabela da página. Para deixar mais claro: eles não respeitam a largura das colunas da página, por que estão fora das colunas da página. Então se você precisar fazer os elementos do DOCUMENT, HEADER e FOOTER, ficarem alinhados com o resto da página, calcule e use o atributo WIDTH em todos os seus elementos COL.

Então lá vamos nós, com o arquivo sales6.xml:
<?xml version="1.0" encoding="ISO-8859-1" standalone="no"?>
<!DOCTYPE REPORT SYSTEM "PHPReport.dtd">
<REPORT>
   <TITLE>Sales Report</TITLE>
   <BACKGROUND_COLOR>#FFFFFF</BACKGROUND_COLOR>
   <CSS>phpreports.css</CSS>
   <DOCUMENT>
      <FOOTER BORDER="1" CELLSPACING="0" CELLPADDING="5" WIDTH="500">
         <ROW>
            <COL ALIGN="RIGHT" CELLCLASS="DOCUMENT_LAYER" WIDTH="430">report total</COL>
            <COL ALIGN="LEFT" CELLCLASS="DOCUMENT_LAYER" TEXTCLASS="BOLD" NUMBERFORMATEX="2" TYPE="EXPRESSION">$this->getSum("value")</COL>
         </ROW>
      </FOOTER>		
   </DOCUMENT>
   <PAGE BORDER="1" SIZE="25" CELLSPACING="0" CELLPADDING="5" WIDTH="500">
      <HEADER>
         <ROW>
            <COL COLSPAN="4" CELLCLASS="PAGE_LAYER" TEXTCLASS="BOLD">John Doe Enterprises</COL>
         </ROW>
         <ROW>
            <COL COLSPAN="4" CELLCLASS="PAGE_LAYER" TEXTCLASS="BOLD">Sales Report</COL>
         </ROW>
      </HEADER>		
      <FOOTER>
         <ROW>
            <COL ALIGN="RIGHT" COLSPAN="3" CELLCLASS="PAGE_LAYER">page total</COL>
            <COL ALIGN="LEFT" NUMBERFORMATEX="2" CELLCLASS="PAGE_LAYER" TEXTCLASS="BOLD" TYPE="EXPRESSION">$this->getSum("value")</COL>
         </ROW>
      </FOOTER>		
   </PAGE>
   <GROUPS>
      <GROUP NAME="citygroup" EXPRESSION="city">
         <HEADER>
            <ROW>
               <COL CELLCLASS="GROUP_LAYER">city:</COL>
               <COL CELLCLASS="GROUP_LAYER" TEXTCLASS="BOLD" TYPE="EXPRESSION" COLSPAN="3">$header->getValue("city")</COL>
            </ROW>
            <ROW>
               <COL CELLCLASS="GROUP_LAYER">id</COL>
               <COL CELLCLASS="GROUP_LAYER">name</COL>
               <COL CELLCLASS="GROUP_LAYER">product</COL>
               <COL CELLCLASS="GROUP_LAYER">$</COL>
            </ROW>
         </HEADER>
         <FOOTER>
            <ROW>
               <COL ALIGN="RIGHT" COLSPAN="3" CELLCLASS="GROUP_LAYER">city total</COL>
               <COL ALIGN="LEFT" CELLCLASS="GROUP_LAYER" TEXTCLASS="BOLD" NUMBERFORMATEX="2" TYPE="EXPRESSION">$this->getSum("value")</COL>
            </ROW>
         </FOOTER>
         <GROUP NAME="customergroup" EXPRESSION="name">
            <FOOTER>
               <ROW>
                  <COL ALIGN="RIGHT" COLSPAN="3" CELLCLASS="GROUP_LAYER">customer total</COL>
                  <COL ALIGN="LEFT" CELLCLASS="GROUP_LAYER" TEXTCLASS="BOLD" NUMBERFORMATEX="2" TYPE="EXPRESSION">$this->getSum("value")</COL>
               </ROW>
            </FOOTER>
            <FIELDS>
               <ROW>
                  <COL TYPE="FIELD" CELLCLASS="GROUP_LAYER">id</COL>
                  <COL TYPE="FIELD" CELLCLASS="GROUP_LAYER">name</COL>
                  <COL TYPE="FIELD" CELLCLASS="GROUP_LAYER">product</COL>
                  <COL TYPE="FIELD" CELLCLASS="GROUP_LAYER" NUMBERFORMATEX="2">value</COL>
               </ROW>
            </FIELDS>
         </GROUP>
      </GROUP>
   </GROUPS>
</REPORT>
Agora se parece com isso:

sales.xml result file