quinta-feira, 1 de setembro de 2016

[ADVPL] Desenvolvendo classes


Fala galera!

Uma das coisa mais interessantes de se aprender durante o desenvolvimento de algumas rotinas em ADVPL, é o uso de classes. Confesso que para mim isso é um pouco recente, mas estou aplicando sempre que possível em meus fontes.




Com o uso de classes, você tem a possibilidade de reduzir as linhas código do seu fonte, evitar repetição de funções e centralizar uma atividade específica em um único arquivo.
Para ficar melhor exemplificado, vou passar à vocês uma forma que utilizei para simplificar/facilitar o processo de envio de emails (worflow) do Protheus. Anteriormente em nossa base havia um fonte onde possuía uma função de usuário que era chamada sempre que fosse necessário enviar os e-mails, ou seja,  em meio ao meu código fonte - caso eu precisasse enviar e-mail - chamaria a função desta forma:
user function envioMail(cMsg, cDe, cPara, cAssunto)

local cCc := ""
local cBcc := ""
local cAnexo := ""
local cTitulo := ""

cEnvMail := u_EnvMail(  cDe,;      //cDe - origem
   cPara,;    //cPara
   cCc,;    //cCc
   cBcc,;     //cBCC
   cAssunto,; //cTitulo
   cAssunto,; //cAssunto
   cAnexo,;   //cAnexo
   cMsg)    //cMsg

return

A função envioMail() é do fonte que estou desenvolvendo e para enviar o e-mail, preciso "chamar" a outra função (outro fonte/arquivo) chamado u_EnvMail() definindo todos os parâmetros.
Tudo bem! Desta forma é funcional, mas nosso objetivo é tornar o acesso a atividade de envio de e-mail através de uma classe, onde poderíamos - futuramente - trabalhar com objetos e configurar outras funções para nossa classe.

Vamos aos códigos!

Primeiramente vamos adicionar um novo arquivo ao projeto e salvá-lo com o nome ClassMail e adicionaremos o código abaixo:

class ClassMail

      //dados de conexão com o server
      data cServer    //Nome do servidor de envio de e-mail
      data cEmail     //Conta a ser utilizada no envio de e-mail
      data cEmailA    //Usuário para autenticação no servidor de e-mail
      data cPass      //Senha para autenticação no servidor de e-mail
      data lAuth      //Verifica se servidor necessita de autenticação
      data lResulConn //Flag de conexão com o servidor
      data lResulSend //Flag de sucesso de envio de e-mail
      data cError     //Mensagem de erro

      //variáveis para envio do email
      data cDe      //E-mail de origem
      data cPara    //E-mail de destino
      data cCc      //E-mail de cópia
      data cBcc     //E-mail de cópia oculta
      data cTitulo  //Título do e-mail
      data cAssunto //Assunto do e-mail
      data cAnexo   //Arquivo que será anexado
      data cMsg     //Mensagem/Conteúdo do e-mail

      //metodo construtor da classe com as variáveis que são enviadas por parâmetro
      method new(cDe, cPara, cCc, cBcc, cTitulo, cAssunto, cAnexo, cMsg) CONSTRUCTOR

      //método de validação dos dados recebidos
      method valDados()

      //método de validação de conexão com o servidor de envio de e-mail
      method valConn()

      //método responsável para efetuar o envio do e-mail
      method sendMail()

endClass
Nesse primeiro ponto, temos a definição das variáveis e métodos de nossa classe. Temos as variáveis responsáveis para efetuar, autorizar e obter o retorno de conexão com o servidor de e-mails e as variáveis que receberemos por parâmetro através do método construtor da nossa classe. depois temos os métodos da classe que utilizaremos durante a execução como validação de dados, conexão e o envio de e-mail.

Em nosso método construtor, vamos receber os dados e atribuir as variáveis da nossa classe. Observe que, quando for referenciar uma variável da classe deve-se utilizar o '::' na frente dela, ou seja, seria algo como ::cEmail. É importante também adicionar ao final do método o nome da classe.

Os métodos podem e devem obter um retorno, somente o tipo do conteúdo/variável que será retornado dependendo da sua aplicação. Veja que em nosso método construtor, não há necessidade retorno, então inserimos a palavra reservada self.

method new(cDe, cPara, cCc, cBcc, cTitulo, cAssunto, cAnexo, cMsg) class ClassMail

 ::cDe      := cDe
 ::cPara    := cPara
 ::cCc      := cCc
 ::cBcc     := cBcc
 ::cTitulo  := cTitulo
 ::cAssunto := cAssunto
 ::cAnexo   := cAnexo
 ::cMsg     := cMsg

return self

Agora vamos aplicar a validação dos dados principais de nossa classe. através do método valDados() Não é possível enviar um e-mail sem um e-mail de origem, destino e assunto - ou qualquer outro campo que você desejar.

method valDados() class ClassMail

local lRet := .T.

if empty(::cDe) .or. empty(::cPara) .or. empty(::cAssunto)
 msgAlert("Os campos 'De','Para' ou 'Assunto' devem ser preenchidos corretamente!","ERRO")
 lRet := .F.
endIf

return lRet

Precisamos agora do método responsável por efetuar a conexão com o servidor de e-mails e, para isso, vamos definir outro método nomeado valConn().

method valConn() class ClassMail

local lRet := .T.

::cServer := allTrim(getMv("MV_RELSERV")) //Nome do servidor de envio de e-mail
::cEmail  := allTrim(getMv("MV_RELACNT")) //Conta a ser utilizada no envio de e-mail
::cEmailA := allTrim(getMv("MV_RELAUSR")) //Usuário para autenticação no servidor de e-mail
::cPass   := allTrim(getMv("MV_RELAPSW")) //Senha para autenticação no servidor de e-mail
::lAuth   := getMv("MV_RELAUTH") //Verifica se servidor necessita de autenticação

if (empty(::cServer) .or. empty(::cEmail) .or. empty(::cEmailA) .or. empty(::cPass)) .and. ::valDados()
 msgAlert("Não foi possível obter os dados dos parâmetros de envio de e-mail do Protheus!","Erro envio de e-mail")
 lRet := .F.
else
 //Efetua a conexão com o servidor
 CONNECT SMTP SERVER ::cServer ACCOUNT ::cEmail PASSWORD ::cPass RESULT ::lResulConn

 //se necessita a autenticação do servidor
 if ::lAuth
  lOk := MailAuth(::cEmailA, ::cPass)
  if !lOk
   lOk := QAGetMail()
  endIf
 endIf

 if !::lResulConn
  get MAIL ERROR ::cError
  msgAlert("Falha na conexão: " + ::cError,"Erro - Problema de conexão")
  lRet := .F.
 endIf
endIf

return lRet

Depois desses métodos desenvolvidos precisamos fazer o envio do e-mail. Então o método sendMail() fará isso para nós.

method sendMail() class ClassMail

//se as validações (conexão e dados) estiverem ok. Efetua o envio do e-mail
if ::valConn() .and. ::valDados()

 SEND MAIL from ::cDe;
  to         ::cPara;
  CC         ::cCc;
  BCC        ::cBcc;
  SUBJECT    ::cAssunto;
  BODY       ::cMsg;
  ATTACHMENT allTrim(::cAnexo);
  RESULT     ::lResulSend

 get MAIL ERROR ::cError

 if !::lResulSend
  msgAlert("Falha no envio do e-mail: " + ::cError,"Erro - E-mail não enviado")
 endif

 DISCONNECT SMTP SERVER
else
 msgInfo("Email não enviado!")
endIf

return self

Bom, com o intuito de evitar tentativa de conexão desnecessária, condicionaremos essa ação ao retorno dos métodos valConn() e valDados() - Note que, assim como as variáveis, os métodos devem ser tratados dentro da classe com '::'.

Assim que testarmos o nosso fonte e compilarmos, podemos substituir a chamada na função original onde precisamo enviar o email. Então ele ficará modificado da seguinte forma:

user function envioMail(cMsg, cDe, cPara, cAssunto)

local cCc := ""
local cBcc := ""
local cAnexo := ""
local cTitulo := ""

objMail := ClassMail():new(cDe, cPara, cCc, cBcc, cTitulo, cAssunto, cAnexo, cMsg)
objMail:sendMail()

return

Em nossa função de usuário teremos um objeto - objMail - que será uma instancia da nossa classe ClassMail() e passará em seu método construtor as informações para o envio do e-mail. Após isso, basta chamar o método sendMail(). 

Com o desenvolvimento de classes podemos incrementar nossas possibilidades de uso de funções e aplicações específicas do sistema, tornando mais fácil o desenvolvimento e manutenção do fonte. Claro que aqui foi dado um exemplo simples, mas é possível adicionar mais funcionalidades de acordo com o que precisar.

Até mais!

;)
Renan Rodrigues Ramos Desenvolvedor

Sou desenvolvedor, amo desenvolvimento de software, músico e apaixonado por filmes e seriados.