Meu primeiro fonte MVC
A arquitetura Model-View-Controller ou MVC, como é mais conhecida, é um padrão de arquitetura de software que visa separar a lógica de negócio da lógica de apresentação, permitindo desenvolvimento, teste e manutenção isolados.Aqueles que já desenvolveram uma aplicação em AdvPL vão perceber, que justamente a diferença mais importante entre a forma de construir uma aplicação em MVC e a forma tradicional é essa separação, que permitirá usar a regra de negócio em aplicações que tenham ou não interfaces, como WebServices e aplicação automática e seu reuso em outras aplicações.
As funções de uma aplicação MVC são.
- ModelDef: Contém a regra de negocio;
- ViewDef: Contém a interface;
- MenuDef: Opções do menu (aRotina).
Conhecendo bem MVC, na verdade a Totvs está quase chegando lá, de certa forma usando o Desenhador de MVC facilitou e muito, apesar de ainda não termos muito material disponível para fazermos coisas bem mais complexas, mas já está bem tranquilo desenvolver nossos fontes como por exemplo o Modelo 2 e o Modelo 3.
Irei postar vários artigos demonstrando como desenvolver vários tipo de rotinas, lembrando que assim como você e outras centenas de profissionais, estou aprendendo a utilizar esta nova forma. E vamos nessa…
Neste primeiro exemplo, irei demonstrar como abrir um browser com a tabela SX5, eu poderia está fazendo com qualquer outra, mas achei mais interessante usar esta para poder fazer algumas validaçõesinhas. Será algo simples, nos próximos artigos irei colocar exemplos mais complexos.
Irei montar o browser usando as funções Incluir, Alterar, Excluir, Copiar, Imprimir e Visualizar padrão do Protheus. Usando a tabela SX5, irei montar um browser filtrando pela sub tabela 21.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 |
/*Importar as bibliotecas*/ #INCLUDE "PROTHEUS.CH" #INCLUDE "FWMBROWSE.CH" #INCLUDE "FWMVCDEF.CH" /*Iniciando sua função*/ User Function MVC001() /*Declarando as variáveis que serão utilizadas*/ Local lRet := .T. Local aArea := SX5->(GetArea()) Private oBrowse Private cChaveAux := "" //Iniciamos a construção básica de um Browse. oBrowse := FWMBrowse():New() //Definimos a tabela que será exibida na Browse utilizando o método SetAlias oBrowse:SetAlias("SX5") //Definimos o título que será exibido como método SetDescription oBrowse:SetDescription("Grupo Tributário") //Adiciona um filtro ao browse oBrowse:SetFilterDefault( "X5_TABELA == '21'" ) //Desliga a exibição dos detalhes //oBrowse:DisableDetails() //Ativamos a classe oBrowse:Activate() RestArea(aArea) Return |
1 2 3 4 5 6 7 8 9 10 11 12 13 |
//------------------------------------------------------------------- // Montar o menu Funcional //------------------------------------------------------------------- Static Function MenuDef() Local aRotina := {} ADD OPTION aRotina TITLE "Pesquisar" ACTION 'PesqBrw' OPERATION 1 ACCESS 0 ADD OPTION aRotina TITLE "Visualizar" ACTION "VIEWDEF.MVC001" OPERATION 2 ACCESS 0 ADD OPTION aRotina TITLE "Incluir" ACTION "VIEWDEF.MVC001" OPERATION 3 ACCESS 0 ADD OPTION aRotina TITLE "Alterar" ACTION "VIEWDEF.MVC001" OPERATION 4 ACCESS 0 ADD OPTION aRotina TITLE "Excluir" ACTION "VIEWDEF.MVC001" OPERATION 5 ACCESS 0 ADD OPTION aRotina TITLE "Imprimir" ACTION "VIEWDEF.MVC001" OPERATION 8 ACCESS 0 ADD OPTION aRotina TITLE "Copiar" ACTION "VIEWDEF.MVC001" OPERATION 9 ACCESS 0 Return aRotina |
A função ViewDef define como o será a interface e portanto como o usuário interage com o modelo de dados (Model) recebendo os dados informados pelo usuário, fornecendo ao modelo de dados (definido na ModelDef) e apresentando o resultado.
A interface pode ser baseada totalmente ou parcialmente em um metadado (dicionário), permitindo:
Reaproveitamento do código da interface, pois uma interface básica pode ser acrescida de novos componentes;
Simplicidade no desenvolvimento de interfaces complexas. Um exemplo disso são aquelas aplicações onde uma GRID depende de outra. No MVC a construção de aplicações que tem GRIDs dependentes é extremamente fácil;
Agilidade no desenvolvimento, a criação e a manutenção se tornam muito mais ágeis;
Mais de uma interface por Bussiness Object. Poderemos ter interfaces diferentes para cada variação de um segmento de mercado, como o varejo.
A ViewDef deve ser uma Static Function dentro da aplicação.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 |
Static Function ViewDef() Local oView Local oModel := ModelDef() Local oStr1:= FWFormStruct(2, 'SX5') // Cria o objeto de View oView := FWFormView():New() // Define qual o Modelo de dados será utilizado oView:SetModel(oModel) //Adiciona no nosso View um controle do tipo FormFields(antiga enchoice) oView:AddField('Formulario' , oStr1,'CamposSX5' ) //Remove os campos que não irão aparecer oStr1:RemoveField( 'X5_DESCENG' ) oStr1:RemoveField( 'X5_DESCSPA' ) // Criar um "box" horizontal para receber algum elemento da view oView:CreateHorizontalBox( 'PAI', 100) // Relaciona o ID da View com o "box" para exibicao oView:SetOwnerView('Formulario','PAI') oView:EnableTitleView('Formulario' , 'Grupo Tributário' ) oView:SetViewProperty('Formulario' , 'SETCOLUMNSEPARATOR', {10}) //Força o fechamento da janela na confirmação oView:SetCloseOnOk({||.T.}) Return oView |
A função ModelDef define a regra de negócios propriamente dita onde são definidas:
Todas as entidades (tabelas) que farão parte do modelo de dados (Model);
Regras de dependência entre as entidades;
Validações (de campos e aplicação);
Persistência dos dados (gravação).
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 |
Static Function ModelDef() Local oModel Local oStr1:= FWFormStruct( 1, 'SX5', /*bAvalCampo*/,/*lViewUsado*/ ) // Construção de uma estrutura de dados //Cria o objeto do Modelo de Dados //Irie usar uma função MVC001V que será acionada quando eu clicar no botão "Confirmar" oModel := MPFormModel():New('GrupoTributario', /*bPreValidacao*/, { | oModel | <strong><em><span style="color: #ff0000;">MVC001V</span></em></strong>( oModel ) } , /*{ | oMdl | MVC001C( oMdl ) }*/ ,, /*bCancel*/ ) oModel:SetDescription('Grupo Tributário') //Abaixo irei iniciar o campo X5_TABELA com o conteudo da sub-tabela oStr1:SetProperty('X5_TABELA' , MODEL_FIELD_INIT,{||'21'} ) //Abaixo irei bloquear/liberar os campos para edição oStr1:SetProperty('X5_TABELA' , MODEL_FIELD_WHEN,{|| .F. }) //Podemos usar as funções INCLUI ou ALTERA //oStr1:SetProperty('X5_CHAVE' , MODEL_FIELD_WHEN,{|| INCLUI }) //Ou usar a propriedade GetOperation que captura a operação que está sendo executada oStr1:SetProperty("X5_CHAVE" , MODEL_FIELD_WHEN,{|oModel| oModel:GetOperation()== 3 }) oStr1:RemoveField( 'X5_DESCENG' ) oStr1:RemoveField( 'X5_DESCSPA' ) oStr1:RemoveField( 'X5_FILIAL' ) // Adiciona ao modelo uma estrutura de formulário de edição por campo oModel:addFields('CamposSX5',,oStr1,{|oModel|<strong><span style="color: #ff0000;"><em>MVC001T</em></span></strong>(oModel)},,) //Define a chave primaria utilizada pelo modelo oModel:SetPrimaryKey({'X5_FILIAL', 'X5_TABELA', 'X5_CHAVE' }) // Adiciona a descricao do Componente do Modelo de Dados oModel:getModel('CamposSX5'):SetDescription('TabelaSX5') Return oModel |
1 2 3 4 5 6 7 8 9 |
//Esta função será executada no inicio do carregamento da tela, neste exemplo irei //apenas armazenar numa variável o conteudo de um campo Static Function MVC001T( oModel ) Local lRet := .T. Local oModelSX5 := oModel:GetModel( 'CamposSX5' ) cChaveAux := SX5->X5_CHAVE Return(lRet) |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 |
//------------------------------------------------------------------- // Validações ao salvar registro // Input: Model // Retorno: Se erros foram gerados ou não //------------------------------------------------------------------- Static Function MVC001V( oModel ) Local lRet := .T. Local oModelSX5 := oModel:GetModel( 'CamposSX5' ) Local nOpc := oModel:GetOperation() Local aArea := GetArea() //Capturar o conteudo dos campos Local cChave := oModelSX5:GetValue('X5_CHAVE') Local cTabela := oModelSX5:GetValue('X5_TABELA') Local cDescri := oModelSX5:GetValue('X5_DESCRI') Begin Transaction if nOpc == 3 .or. nOpc == 4 if Empty(cTabela) oModelSX5:SetValue('X5_TABELA','21') Endif dbSelectArea("SX5") SX5->(dbSetOrder(1)) SX5->(dbGoTop()) If(SX5->(dbSeek(xFilial("SX5")+cTabela+cChave))) if cChaveAux != cChave SFCMsgErro("A chave "+Alltrim(cChave)+" ja foi informada!","MVC001") lRet := .F. Endif Endif if Empty(cChave) SFCMsgErro("O campo chave é obrigatório!","MVC001") lRet := .F. Endif if Empty(cDescri) SFCMsgErro("O campo descrição é obrigatório!","MVC001") lRet := .F. Endif Endif if !lRet DisarmTransaction() Endif End Transaction RestArea(aArea) FwModelActive( oModel, .T. ) Return lRet |
Como pode ver, não é tão difícil começar a desenvolver em MVC.
3 comentários
Olá, muito bom seu arquivo. Mas tô com uma dificuldade aqui, como mostrar os valores de campos virtuais no grid da tabela detalhe ?
“Browse inic.” não fuciona
Segue exemplo usando MVC com tabela que possui campo virtual https://blogadvpl.com/criando-tela-em-mvc-separando-por-grupos-e-inserindo-novos-botoes/
[…] no primeiro exemplo postado em Meu primeiro fonte MVC , iremos mostrar como montar uma tela onde iremos separar os campos por grupos e incluiremos um […]