Abstração de Acesso a Dados e Orientação a Objetos – Parte 03

Introdução

Nos posts anteriores (Abstração de Acesso a Dados e Orientação a Objetos – Parte 02,Abstração de Acesso a Dados e Orientação a Objetos), vimos a montagem de um encapsulamento de acesso a dados usando orientação a objetos com herança em AdvPL. Agora, vamos integrar esse mecanismo com um Alias / WorkArea do AdvPL.

Criando uma Tabela ( DBF ou MEMORY ) a partir de um ALIAS

Usando as classes de arquivo em memória (ZMEMFILE) ou arquivo DBF (ZDBFFILE), podemos criar uma tabela (em memória ou no disco) com a mesma estrutura de uma outra tabela de uma destas classes, informando como parâmetro o objeto. Que tal ela também receber um ALIAS como parâmetro ? Pode ser de uma tabela qualquer, não importa. E, melhor ainda, que tal este método receber um segundo parâmetro, que caso seja especificado .T. (verdadeiro), já abre a tabela criada em modo exclusivo e copia todos os dados do ALIAS informado como parâmetro? Veja o exemplo abaixo:

#include 'protheus.ch"

User Function QRY2MEM()
Local cQuery
Local nH

nH := tclink()
IF nH < 0
  MsgStop("TCLINK ERROR - "+cValToChar(nH))
  Return
Endif

// Cria um select statement
cQuery := "SELECT CPF , NOME, VALOR from DOADORES WHERE VALOR > 2000 order by 3 DESC"

// Abre a Query no alias QRY 
USE (tcGenQry(,,cQuery)) ALIAS QRY SHARED NEW VIA "TOPCONN"

// Ajusta um campo 
TCSetField("QRY","VALOR","N",12,2)

// Cria um objeto de arquivo em memoria 
oMemory := ZMEMFILE():New('QRYINMEMORY')

// Popula o objeto com a estrutura e dados da Query 
// Passando o ALIAS como parâmetro. 
oMemory:CreateFrom("QRY",.T.)

// Mostra o conteudo da tabela na memoria. 
// Nao precisa abrir o objeto, o CreateFrom() já fez isso
While !oMemory:Eof()
  conout(oMemory:Fieldget(1)+" "+oMemory:FieldGet(2)+" "+cValToChar(oMemory:FieldGet(3))) 
  oMemory:Skip()
Enddo

// Fecha e mata a tabela
oMemory:Close()
FreeObj(oMemory)

// fecha a query
USE

return

Agora sim a coisa ficou prática. E, usando esta abordagem, eu tenho algumas vantagens incríveis. Primeira, com a Query copiada para a memória, usando o arquivo em memória eu posso mexer nos dados, eu posso inserir novos registros, posso navegar para frente e para trás ( Skip -1 ), e tudo o mais que com um ALIAS a partir de uma Query, nada disso é possível de ser feito.

O método CreateFrom() trabalha em conjunto com o AppendFrom(), ambos do ZISAMFILE. Uma vez determinado que eles receberam uma string ao invés de um Objeto como parâmetro, eles assumem que a string contém um ALIAS de uma WorkArea aberta, e fazem a leitura de estrutura e dados do ALIAS informado.

Métodos CreateFrom e AppendFrom

METHOD CreateFrom( _oDBF , lAppend  ) CLASS ZISAMFILE
Local lFromAlias := .F. 
Local cAlias := ""
Local aStruct := {}

If lAppend = NIL ; lAppend := .F. ; Endif

If valtype(_oDBF) == 'C'
	// Se a origem é caractere, só pode ser um ALIAS 
	lFromAlias := .T. 
	cAlias := alltrim(upper(_oDBF))
	If Select(cAlias) < 1 
		UserException("Alias does not exist - "+cAlias)
	Endif
	aStruct := (cAlias)->(DbStruct())
Else
	aStruct := _oDBF:GetStruct()
Endif

If !::Create(aStruct)
	Return .F.
Endif

IF lAppend
	// Dados serão apendados na criação 
	// Abre para escrita exclusiva 
	If !::Open(.T.,.T.)
		Return .F.
	Endif
	// Apenda os dados	
	IF !::AppendFrom(_oDBF)
		Return .F.
	Endif
	// E posiciona no primeiro registro 	
	::GoTop()
Endif
Return .T.
METHOD AppendFrom( _oDBF , lAll, lRest , cFor , cWhile ) CLASS ZISAMFILE
Local aFromTo := {}
Local aFrom := {}
Local nI, nPos, cField
Local lFromAlias := .F. 
Local cAlias := ""

DEFAULT lAll  := .T. 
DEFAULT lRest := .F.
DEFAULT cFor := ''
DEFAULT cWhile := ''
              
// Primeiro, a tabela tem qye estar aberta
IF !::lOpened
	UserException("AppendFrom Failed - Table not opened")
	Return .F.
Endif

IF !::lCanWrite
	UserException("AppendFrom Failed - Table opened for READ ONLY")
	Return .F.
Endif

If valtype(_oDBF) == 'C'
	// Se a origem é caractere, só pode ser um ALIAS 
	lFromAlias := .T. 
	cAlias := alltrim(upper(_oDBF))
	If Select(cAlias) < 1 
		UserException("Alias does not exist - "+cAlias)
	Endif
	aFrom := (cAlias)->(DbStruct())
Else
	aFrom := _oDBF:GetStruct()
Endif

// Determina match de campos da origem no destino 
For nI := 1 to len(aFrom)
	cField :=  aFrom[nI][1]
	nPos := ::FieldPos(cField)
	If nPos > 0 
		aadd( aFromTo , { nI , nPos })
	Endif
Next

IF lFromAlias
	// Dados de origem a partir de uma WorkArea
	If lAll 
		// Se é para importar tudo, pega desde o primeiro registro 
		(cAlias)->(DbGoTop())
	Endif
	While !(cAlias)->(EOF())
		// Insere um novo registro na tabela atual
		::Insert()
		// Preenche os campos com os valores da origem
		For nI := 1 to len(aFromTo)
			::FieldPut(  aFromTo[nI][2] , (cAlias)->(FieldGet(aFromTo[nI][1]))  )
		Next
		// Atualiza os valores
		::Update()
		// Vai para o próximo registro
		(cAlias)->(DbSkip())
	Enddo
	
Else
	If lAll 
		// Se é para importar tudo, pega desde o primeiro registro 
		_oDBF::GoTop()
	Endif
	While !_oDBF:EOF()
		// Insere um novo registro na tabela atual
		::Insert()
		// Preenche os campos com os valores da origem
		For nI := 1 to len(aFromTo)
			::FieldPut(  aFromTo[nI][2] , _oDBF:FieldGet(aFromTo[nI][1])  )
		Next
		// Atualiza os valores
		::Update()
		// Vai para o próximo registro
		_oDBF:Skip()
	Enddo
Endif
Return .T.

O CreateFrom() permite criar a tabela apenas com a estrutura, porém se parametrizado com .T. no segundo parâmetro, já abre a tabela atual e importa os dados do objeto ou ALIAS de origem especificado.

Próximos passos

Criando mais alguns encapsulamentos, será possível colocar de modo mais prático uma tabela em Cache. Eu posso criar uma tabela temporária em memória com o resultado de uma Query, e colocar esta tabela no cache, para ser recuperada conforme a necessidade. Lembrando que esta tabela não deve ser monstruosa, mas ter um número de registros que isole um contexto. Senão você armazena um caminhão de tijolos no Cache, mas quando você resgata  o cache para uso, você usa apenas alguns tijolos.

Conclusão

Nada a declarar. Fontes da zLib atualizados no GITHUB, agora é só bolar um encapsulamento neste mesmo padrão para uma tabela ISAM do DBAccess e para uma Query, depois fazer Export e Import para outros formatos (JSON, TXT, SFD, CSV , XML, “Socorro”).

Desejo a todos um bom proveito desta implementação, e TERABYTES DE SUCESSO !!!

 

Deixe um comentário

Preencha os seus dados abaixo ou clique em um ícone para log in:

Logotipo do WordPress.com

Você está comentando utilizando sua conta WordPress.com. Sair /  Alterar )

Foto do Google

Você está comentando utilizando sua conta Google. Sair /  Alterar )

Imagem do Twitter

Você está comentando utilizando sua conta Twitter. Sair /  Alterar )

Foto do Facebook

Você está comentando utilizando sua conta Facebook. Sair /  Alterar )

Conectando a %s