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.

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.
/*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
//-------------------------------------------------------------------
// 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.
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).
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 | MVC001V( 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|MVC001T(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
//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)
//-------------------------------------------------------------------
// 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.