2. 243
Vamos supor que a partir de nosso Billing System, queremos enviar um sistema de devedores e credores, uma
listagem dos recibos correspondentes ao último faturamento. Se trata de informação hierárquica (a informação
a ser enviada é a de recibos, cada um dos quais possui determinados dados).
XML é o formato mais usual de troca de informação hierárquica, ainda não sabemos o que vai acontecer no
futuro neste aspecto (por exemplo se vai voltar a ser um formato comum ou formato Json).
3. 244
Tendo o mesmo SDT Bill que definimos quando estudamos os tipos de dados estruturados (ali o chamamos
Bill SDT, aqui o chamaremos Bill), estamos utilizando um procedimento com os seguintes parâmetros:
parm( in: &start, in: &end, out: &bills );
sendo &start e &end variáveis de tipo Date que recebem a faixa de faturamento, e &bills uma variável collection
do SDT Bill.
Observe que estamos utilizando o Data Selector ActiveCustomers para filtrar pelos clientes ativos e utilizando a
fórmula inline sum, onde unicamente somamos os totais daquelas faturas que pertencem ao cliente da atual
iteração do For each e cujas datas se encontrem na faixa informada e tenham o valor True no atributo
Booleano InvoicePendingFlag.
Este atributo será alterado a False uma vez que a fatura foi processada dentro do processo de geração de
recibos que iniciamos aqui mas que completaremos mais adiante, quando estudarmos as formas de atualizar a
informação da base de dados (aqui somente estamos obtendo a informação dos recibos, mas ainda não os
registraremos na base de dados... ).
4. 245
Todo procedimento toma um Input, e mediante alguma transformação,obtêm um Output.
Para obter o Input tendo certa linguagem (em GeneXus se trata da base de dados, basta nomear os atributos,
como pode ser visto no exemplo, a direita das atribuições; assim como também se obtêm os parâmetros).
Para realizar a Transformação possui outra linguagem (o código que podemos ver no exemplo) e depois para
expressar o Output outro mais.
Como pode ver-se, no caso dos procedimentos GeneXus, tanto o Input como o Output se encontram situados
dentro do próprio código de Transformação. Pode se dizer que o foco está colocado na transformação.
Desta forma o propósito do procedimento, sua saída, fica obscura dentro desse código, que mescla:
• elementos de saída,
• com elementos de transformação (em nosso caso, o comando for each, o método add e o operador
new, que implementam o armado da coleção de recibos)
• e com elementos de entrada.
5. 246
Com um Data Provider, o foco está situado na linguagem de saída: observe que é indicado numa estrutura
hierárquica do que é composto esse Output.
Depois, para cada elemento da estrutura hierárquica, terá que indicar no Source do Data Provider, como se
calcula.
Portanto, basta observar o lado esquerdo para deduzir qual será a estrutura resultante.
Depois, é suficiente indicar o formato desejado para essa estrutura hierárquica resultante...
6. 247
Depois, uma mesma informação estruturada, pode ser representada utilizando diferentes formatos existentes.
Essa é a idéia do Data Provider. Se num futuro aparecer um novo formato de representação de informação
estruturada, o Data Provider continuará sendo o mesmo. GeneXus implementará o método de transformação
para esse formato, e somente terá que utilizá-lo.
7. 248
Até aqui fizemos uma análise descritiva. Surge a seguinte pergunta: Como em GeneXus representamos as
estruturas hierárquicas de dados?
A saída de um Data Provider será um SDT (ou uma coleção de SDTs).
Depois, com essa saída se realiza o desejado, em particular, convertê-la ao formato XML, com o método toXml
dos SDTs.
Vejamos como declaramos a saída... não será da forma convencional (como parâmetro de out da regra
parm)...
8. 249
Aqui vemos para o exemplo que estamos estudando, que tendo o SDT definido Bills, collection, que coincide
com a estrutura que se infere do Source do Data Provider ‘GetBills’, terá que declarar esse SDT na
propriedade Output do Data Provider.
Mas não é a única possibilidade ... veremos outra na página seguinte, onde terá sentido a propriedade
Collection.
9. 250
Se ao invés de ter o SDT collection Bills, tivéssemos o que aparece acima, Bill, então, poderíamos programar o
Source do Data Provider como vemos e depois configurar a propriedade Collection em ‘True’, e uma nova
propriedade é aberta, Collection Name, que permitirá dar nome para a coleção.
Este Data Provider é equivalente ao anterior.
Observe que neste caso não é necessário colocar a raiz da hierarquia, Bills, que se infere a propriedade
‘Collection Name’. De todas as formas, mesmo não sendo requerido pode-se programar o Source da mesma
forma que o anterior, isto é, com o grupo Bills iniciando a hierarquia.
Bills
{
Bill using ActiveCustomers()
{
...
}
}
Nota: a cláusula using, assim como os where que veremos em seguida, o order, etc., poderão ser
especificadas tanto no grupo Bills, como no grupo Bill, em casos como este. Em seguida voltaremos nesse
tema.
10. 251
Os Data Providers resolvem eficientemente um tipo de problema: aquele que consiste em retornar dados
estruturados. Para esse tipo de problemas contávamos com os procedimentos, mas sua desvantagem evidente
era que necessitávamos implementar a forma de armar essas estruturas com operações de baixo nível, como
agregar um item a uma coleção com Add, e pedir memória (new). Assim mesmo, se tivesse que fazer cálculos
complexos para dar valor a cada membro da estrutura, os elementos de saída estariam incorporados no
código, o resultado do Procedimento não acaba sendo de fácil visualização.
Todos estes inconvenientes são evitados com Data Provider. O foco aqui está na saída, razão pela qual ao
olhar já se sabe a estrutura do Output. Sendo declarativo, livre da forma que está implementada a carga do
SDT. GeneXus se encarrega disso.
Quanto mais declarativo for uma ferramenta, mais fácil de programar, mais adaptável as mudanças, mais
independente de uma implementação particular. GeneXus tende a ser o mais declarativo possível e cada vez
programar menos ‘procedural’. A forma declarativa tira do programador o problema e passa para a
implementação da ferramenta. Quanto mais inteligente for ferramenta, menos o programador precisa resolver o
problema: basta enunciá-lo.
11. 252
Um Data Provider também pode receber parâmetros através da regra parm, mas se diferencia de um
procedimento onde todos os parâmetros podem ser de entrada/saída, aqui somente poderão ser de entrada.
Por outro lado, um procedimento que devolve informação estruturada é feito mediante uma variável que se
carrega no código, e que deve declarar-se como de saída, no último parâmetro da regra parm.
Em um Data Provider, a declaração do tipo de dados de saída se faz mediante as propriedades Output, não na
regra parm.
A chamada a partir de qualquer objeto GeneXus de um Data Provider é idêntica a chamada de um
procedimento, isto é, com udp.
Lembre que ao chamar um objeto e não especificar o método de chamada, se assume udp.
A variável &TheBills deverá estar declarada no objeto GeneXus que se realiza a chamada. É a variável na qual
se devolve o resultado. Lembre que se o SDT definido na KB é o correspondente aos itens individuais: Bill, (e
não a coleção Bills), então a variável &TheBills será definida como Collection, de tipo de dados Bill.
Depois, com a estrutura hierárquica devolvida pode, por exemplo, ser convertida ao formato desejado, como
XML.
Importante:
Como veremos, um Data Provider não somente pode retornar um SDT ou coleção de SDT, como também
outro tipo, o Business Component, que também representa informação estruturada. Olhar esse tema para
completar o conhecimento de Data Providers.
12. 253
O exemplo que podemos ver aqui é parecido ao que trabalhamos. Agregamos o elemento BillQuantity. Tire uns
instantes para pensar como deverá ser o SDT BillsInfo, para que se iguale com este Source. A esquerda o que
apresentamos.
O Source não precisa ser escrito da mesma forma que a estrutura do SDT. Se o SDT ter um membro collection
de um item determinado, podemos omitir do Source o Item como grupo. Exemplo: com o SDT que temos
acima, poderíamos ter escrito o Data Provider:
Billsnfo
{
Bills
{
BillDate = &today
CustomerName = CustomerName
BillInvoicePeriodStartDate = &start
BillInvoicePeriodEndDate = &end
BillAmount = sum (InvoiceAmount, ... )
&quantity = &quantity + 1
}
BillQuantity = &quantity
}
Com o mesmo resultado. GeneXus tem inteligência suficiente para matchear com o SDT.
14. 255
No exemplo, o grupo de nome Bill será repetitivo. Por que? Para responder a pergunta, vamos fazer outra: e se
for um for each, onde substitua os elementos da esquerda das atribuições por variáveis? Neste caso a
presença de CustomerName a direita da segunda atribuição permite afirmar que tem tabela base:
CUSTOMER.
Por tanto o grupo será repetitivo, iterando sobre a tabela CUSTOMER.
No exemplo que estamos trabalhando, temos a cláusula:
Bill using ActiveCustomers()
igual:
For each using ActiveCustomers()
se o atributo CustomerName não estivesse na segunda atribuição, da mesma forma seria com tabela base,
pela presença do atributo CustomerStatus no Data Selector ‘ActiveCustomers’.
Observe que o grupo de nome BillsInfo, de forma diferente, não será repetitivo, não possui cláusulas
associadas, e os elementos que contêm estão definidos a base de variáveis e não de atributos:
BillQuantity = &quantity
&quantity = 0
E o que acontece com o grupo Bills? Observe que neste caso, é um grupo que somente possui outro grupo. O
grupo contido será repetitivo, porque Bills será uma coleção de Bill. Por este motivo, o subgrupo Bill poderia ser
omitido (somente deixar Bills) e que fique implícito. Deste modo, as cláusulas do grupo que permitem definir
order, filtros, defined by, podem ser associadas a este grupo.
15. 256
Se a condição estiver no grupo Clients, seria aplicada para os dois subgrupos Client. Por isso é permitido que
as cláusulas operem nos grupos repetitivos (itens), e não somente do grupo que é coleção de itens.
19. 260
A informação completa desse tema em inglês pode ser encontrada em nossa comunidade wiki, na página:
http://wiki.gxtechnical.com/commwiki/servlet/hwiki?Data+Provider+Language
23. 264
Não mencionamos, mas de forma intuitiva podemos pensar, de ter um par de grupos “aninhados” (um grupo
que, entre outras coisas, contêm outro), se cada um deve acessar a base de dados, então as tabelas base,
assim como o critério de navegação são determinados exatamente da mesma forma que no caso de um par de
for eachs anhidados.
Por esse motivo, no exemplo, o grupo Country possui tabela base COUNTRY; o grupo Customers possui
tabela base CUSTOMER, e terá um join entre ambas tabelas na hora de recuperar a informação para carregar
a coleção de países. Isto é, para cada registro de COUNTRY será carregada a informação de seus atributos
CountryId e CountryName nos elementos Id e Name do item Country do SDT coleção “Countries” que estiver
carregando, e depois percorrerá a tabela CUSTOMER filtrando por aqueles registros para os quais
CUSTOMER.CountryId = COUNTRY.CountryId, carregando para cada um os elementos Id e Name.
A presença da cláusula OutputIfDetail faz que somente se apresentam na saída aqueles países que possuam
clientes associados. Se um país da base de dados não tiver nenhum cliente, então não será apresentado como
item da coleção de saída, “Countries”.