Banco de Dados

14 jan, 2014

Utilizando collections para criação de formulários tabulares no Oracle APEX – Parte 01

Publicidade

Introdução

O APEX, por ser uma ferramenta relativamente nova (estamos apenas na versão 4.2 atualmente), contém algumas limitações, uma delas é que ele não permite que seja criado mais de um formulário tabular por página. Dessa forma, apenas utilizando as funcionalidades core do Oracle APEX, não é possível criar telas com melhor usabilidade. Para contornar essa limitação, devemos criar os formulários tabulares manualmente, utilizando as APIs APEX_ITEM e APEX_COLLECTIONS.

Logo, neste artigo, vou demonstrar como criar formulários tabulares manualmente utilizando as APIs citadas acima. Este artigo exige um conhecimento prévio sobre o APEX, para obter mais informações sobre acesse outros artigos, como este.

Vou demonstrar um cadastro de pessoas, no qual na mesma tela será criado um formulário tabular para cadastrar os endereços e outro para cadastrar os telefones da pessoa. Por ser uma solução um tanto quanto complexa, vou dividir este artigo em três partes. Nesta primeira parte, vou abordar como funcionará a arquitetura da solução e a estrutura necessária para criar o formulário tabular.

Atenção! Este artigo exemplifica como aplicar a solução em questão. Algumas informações, como nome dos itens e tabelas utilizadas, devem ser revistos! Também é possível melhorar a arquitetura dos códigos PL/SQL aqui criados, colocando-os dentro de objetos no banco de dados.

APIs

A API APEX_ITEM permite criar elementos de formulários HTML no padrão APEX facilmente, como CHECKBOX, RADIO BUTTON, DATE_POPUP, TEXT, SELECT LIST, dentre outros.

A API APEX_COLLECTION permite capturar temporariamente vários valores. É possível criar varias collections para guardar informações de linhas e colunas atuais da sessão. Essas informações podem ser acessadas, manipuladas ou processadas durante toda a sessão do usuário. A collection é como uma tabela temporária que pode ser acessada pela chave identificadora da collection na sessão.

Mais informações sobre collection no APEX podem ser encontradas neste link.

Modelo de dados

Para este artigo, vou desenhar um modelo relativamente simples, com apenas três tabelas:

apex-1

TB_PESSOA: Informações das pessoas.

CREATE TABLE  "TB_PESSOA" 
   (	    "ID_PESSOA"         NUMBER NOT NULL ENABLE, 
	    "NOME"              VARCHAR2(60) NOT NULL ENABLE, 
	    "NUMERO_DOCUMENTO"  VARCHAR2(40), 
	    "FORNECEDOR"        VARCHAR2(1), 
	    "RECLAMANTE"        VARCHAR2(1), 
	    "RECLAMADO"         VARCHAR2(1), 
	    "EMPRESA"           VARCHAR2(1), 
	    "ATIVO"             VARCHAR2(1) DEFAULT 'S', 
	    "DATA_INCLUSAO"     DATE, 
	    "USUARIO_INCLUSAO"  VARCHAR2(30), 
	    "DATA_ALTERACAO"    DATE, 
	    "USUARIO_ALTERACAO" VARCHAR2(30), 
	    "DT_NASC_FUNDACAO"  DATE, 
	    "ID_TIPO_DOCUMENTO" NUMBER, 
	    "ADVOGADO"          VARCHAR2(1), 
	    CONSTRAINT "PK_PESSOA" PRIMARY KEY ("ID_PESSOA") ENABLE
   );

TB_TELEFONE: Informações dos telefones das pessoas.

CREATE TABLE  "TB_TELEFONE"
(      "ID_TELEFONE"        NUMBER NOT NULL ENABLE,
"ID_PESSOA"          NUMBER NOT NULL ENABLE,
"NUMERO"             NUMBER,
"DDD"                NUMBER,
"DDI"                NUMBER,
"RAMAL"              NUMBER,
"ATIVO"              VARCHAR2(1) DEFAULT 'S',
"DATA_INCLUSAO"      DATE,
"USUARIO_INCLUSAO"   VARCHAR2(30),
"DATA_ALTERACAO"     DATE,
"USUARIO_ALTERACAO"  VARCHAR2(30),
"DESCRICAO"          VARCHAR2(40),
"PRINCIPAL"          VARCHAR2(1),
CONSTRAINT "PK_TELEFONE" PRIMARY KEY ("ID_TELEFONE") ENABLE
)
/
ALTER TABLE  "TB_TELEFONE" ADD CONSTRAINT "FK_TELEFONE_PESSOA" FOREIGN KEY ("ID_PESSOA")
REFERENCES  "TB_PESSOA" ("ID_PESSOA") ENABLE
/

TB_ENDERECO: Informações dos endereços das pessoas.

 

CREATE TABLE  "TB_ENDERECO"
(      "ID_ENDERECO"         NUMBER NOT NULL ENABLE,
"ID_PESSOA"           NUMBER NOT NULL ENABLE,
"RUA"                 VARCHAR2(60),
"BAIRRO"              VARCHAR2(60),
"CIDADE"              VARCHAR2(60),
"ESTADO"              VARCHAR2(60),
"PAIS"                VARCHAR2(60),
"NUMERO"              VARCHAR2(60),
"COMPLEMENTO"         VARCHAR2(60),
"CEP"                 VARCHAR2(10),
"ATIVO"               VARCHAR2(1) DEFAULT 'S',
"DATA_INCLUSAO"       DATE,
"USUARIO_INCLUSAO"    VARCHAR2(30),
"DATA_ALTERACAO"      DATE,
"USUARIO_ALTERACAO"   VARCHAR2(30),
"DESCRICAO"           VARCHAR2(40),
"PRINCIPAL"           VARCHAR2(1),
CONSTRAINT "PK_ENDERECO" PRIMARY KEY ("ID_ENDERECO") ENABLE
)
/
ALTER TABLE  "TB_ENDERECO" ADD CONSTRAINT "FK_ENDERECO" FOREIGN KEY ("ID_PESSOA")
REFERENCES  "TB_PESSOA" ("ID_PESSOA") ENABLE
/

Entendendo o fluxo da solução

Nesta solução, utilizarei as seguintes tecnologias; APEX, PL/SQL, JAVASCRIPT e AJAX.

O fluxo abaixo demonstra uma visão simplista de como funcionará a solução.

apex-2

  1. Usuário altera o conteúdo do campo do formulário tabular.
  2. É acionada uma função Javascript no evento ONCHANGE do item.
  3. A função javascript chama um bloco PL/SQL utilizando AJAX.
  4. O Bloco PL/SQL atualiza os atributos da collection utilizando a API core APEX_COLLECTION.
  5. Após efetuar todas as alterações, o usuário clica no botão Salvar/Incluir. Neste momento, é executado um processo PL/SQL que lê os dados que já foram atualizados na collection via AJAX e Atualiza/Altera a tabela final no banco de dados. Nesta etapa, é possível efetuar validações dos dados informados pelo usuário.
  6. Os dados estão atualizados no banco de dados.

Para exemplificar o funcionamento da solução, deverão ser criadas duas páginas:

apex-3

Uma página relatório para listar as pessoas cadastradas e outra página formulário para efetuar a manutenção do registro.

Preparando a aplicação

Para que a solução funcione corretamente, deve-se criar um novo “Application Process” de nível de aplicação para atualização da collection.

apex-4

O novo “Application Process” deve obrigatoriamente ser chamado de “setMemberAttribute”, pois é o nome que será chamado através de uma função JavaScript que será criada posteriormente.

apex-5

Na propriedade “Process Text”, coloque o código abaixo:

DECLARE
e_invalid_number exception;
PRAGMA EXCEPTION_INIT(e_invalid_number,-06502);
BEGIN

IF(apex_application.g_x06 = 'N')THEN
   apex_collection.update_member_attribute
(p_collection_name      => apex_application.g_x10,
 p_seq                              => apex_application.g_x09,
 p_attr_number          => apex_application.g_x08,
 p_number_value         => replace(apex_application.g_x07,'.','')
);

ELSIF(apex_application.g_x06 = 'T')THEN

   apex_collection.update_member_attribute
(p_collection_name      => apex_application.g_x10,
 p_seq                  => apex_application.g_x09,
 p_attr_number          => apex_application.g_x08,
 p_attr_value         => apex_application.g_x07
);

ELSIF(apex_application.g_x06 = 'D')THEN

   apex_collection.update_member_attribute
(p_collection_name      => apex_application.g_x10,
 p_seq                  => apex_application.g_x09,
 p_attr_number          => apex_application.g_x08,
 p_date_value         => to_date(apex_application.g_x07,'dd/mm/rrrr')
);

ELSIF(apex_application.g_x06 = 'B')THEN --checkbox

   apex_collection.update_member_attribute
(p_collection_name      => apex_application.g_x10,
 p_seq                  => apex_application.g_x09,
 p_attr_number          => apex_application.g_x08,
 p_attr_value           => CASE WHEN(upper(apex_application.g_x07) = 'TRUE') THEN 'S' ELSE 'N' END
);

END IF;

COMMIT;

EXCEPTION WHEN e_invalid_number THEN

  HTP.P('Número Inválido para o valor "'||apex_application.g_x07||'"!');

          WHEN OTHERS THEN

 HTP.P(SQLERRM);

END;

Esse código está preparado para atualizar as informações de um atributo de uma collection, dependendo do tipo de dado do atributo que será passado pela função Javascript através de AJAX.

Criando a página de listagem de pessoas

Crie uma página simples do tipo Relatório baseada na tabela TB_PESSOA para listar as informações das pessoas cadastradas.

apex-6

Nessa página, crie as opções de editar os registros e incluir novos registros.

Conclusão

Nesta primeira parte do artigo, fiz uma introdução de como a solução irá funcionar e dos principais recursos envolvidos, além de criar o primeiro objeto que será utilizado. Nas próximas partes, irei aprofundar melhor como implementar a solução.

Até o próximo artigo!