«

»

Apr 27

Print this Post

Implementando a Camada ViewHelper

A próxima parte da nossa série de artigos sobre desenvolvimento em camadas é o ViewHelper.
O ViewHelper eu adoto para evitar de misturar html nos meus objetos.
Já vi muita implementação que uma classe retornava HTML, um exemplo seria uma classe que faz uma consulta no banco e já retorna uma >li< populada.
No html você faria:

<cfoutput>
<ul>
	#variables.objeto.getMenu()#
</ul>
</cfoutput>


Primeiro que uma classe de consulta banco não deve retornar String e sim um recordSet. Outra coisa, é que ficaria complicado de fazer modificações na Interface da aplicação, já que em nossas classes temos htmls complicados como o exemplo abaixo:

<cfcomponent>
	<cfscript>
		public Void function init() {
			this.instance.htmlMenu = "";
		}

		public String function getMenu() {
			var q_DadosMenu = "";
			// consulta no banco

			loop query="q_DadosMenu" {
				this.instance.htmlMenu &amp;= "<li class='classMenu'><a title='" &amp; q_DadosMenu.nome &amp; "' href='" &amp; q_DadosMenu.url &amp; "'>" &amp; q_DadosMenu.nome &amp; "</a></li>";
			}

			return this.instance.htmlMeu;
		}
	</cfscript>
</cfcomponent>

Um simples LI é fácil até de editar, mas coisas mais complexas fica bem mais complicado.
Não quero dizer com isso, que o ViewHelper serve para fazer esse tipo de tarefa, pelo contrário. Ele serve para dar suporte ao retorno da camada de controle. Tanto na ida quanto na volta dos dados.
Para fazermos um exemplo prático, vamos sair do modelo de montar um menu.
Vamos a um exemplo de uma busca com mais de um filtro.
Digamos que temos que fazer uma busca de usuários com os filtros: Estado,Cidade e se o cadastro do mesmo está ativo.
E nesse cenário usamos um roteamento de URL manual para melhorar nossas URLs por questões de CEO.
Com isso, nossa busca seria uma URL como a seguinte:

aplicacao/Index.cfm/buscar/7|25|1

Onde aplicação seria o endereço da nossa aplicação no Navegador, Index.cfm a página raiz, buscar seria a ação e 7|25|1 seriam os argumentos da minha ação (Parâmetros de busca)
E entendemos então que na busca os parâmetros serão:

Estado == 7
Cidade == 25
Ativo == 1

Entendendo essas questões, vemos que não seria interessante, enviar para o controller um parâmetro no formato: 7|25|1 descobrir que parâmetros são esses não cabe ao Controller, muito menos ao Model e ao DAO, lembremos do conceito de Divisão de Responsabilidades.

Então vamos usar o ViewHelper para nos ajudar nisso e montar os parâmetros corretamente para o Controller executar as consultas de busca de usuários.

Então em nosso ViewHelper teríamos um método como o exemplo abaixo:

<cfcomponent displayname="ViewHelperBuscaUsuarios" extends="ViewHelperBase">
	<cfscript>
		public ViewHelperBuscaUsuarios function init() {
			super.init();

			this.instance.usuarios = "";

			return this;
		}

		public Query function buscaUsuarios( String parametrosBusca ) {
			var codigoEstado = 0;
			var codigoCidade = 0;
			var isAtivo	 = 0;
			var counter 	 = 1;

			codigoEstado = ListGetAt( arguments.parametrosBusca , counter++ , "|" );
			codigoCidade = ListGetAt( arguments.parametrosBusca , counter++ , "|" );
			isAtivo	     = ListGetAt( arguments.parametrosBusca , counter++ , "|" );

			this.instance.usuarios = this.instance.dao.getController("Usuarios")
							.buscarUsuario( estado : codigoEstado ,
									cidade : codigoCidade ,
									 ativo : isAtivo );

			return this.instance.usuarios;
		}
	</cfscript>
</cfcomponent>

Deu para perceber a necessidade de um helper?
Mas ainda tem mais. Duas coisas temos que observar agora:

  1. Primeiro que teremos que retornar pra view o nome do estado e o nome da cidade onde o usuário reside. Por questões de usabilidade, se existe filtro por estado, devemos ter essa informação no resultado.
  2. Se tivermos como evitar JOINS no DAO, primeiro por desempenho e segundo para não misturar. O DAO é do Usuário e não do Usuário, Estado e Cidade.

Vamos ver abaixo então, como implementaríamos o mesmo método acima, porém incluindo o estado e cidade.

<cfcomponent displayname="ViewHelperBuscaUsuarios" extends="ViewHelperBase">
	<cfscript>
		public ViewHelperBuscaUsuarios function init() {
			super.init();

			this.instance.usuarios = "";

			return this;
		}

		public Struct function buscaUsuarios( String parametrosBusca ) {
			var structDadosUsuarios = [];
			var q_DadosUsuarios 	= "";
			var estadoController	= this.instance.dao.getController("Estados");
			var cidadeController	= this.instance.dao.getController("Cidades");
			var q_DadosEstados	= estadoController.listar();
			var q_DadosCidades	= cidadeController.listar();
			var codigoEstado 	= 0;
			var codigoCidade 	= 0;
			var isAtivo	 	= 0;
			var counter 	 	= 1;

			codigoEstado = ListGetAt( arguments.parametrosBusca , counter++ , "|" );
			codigoCidade = ListGetAt( arguments.parametrosBusca , counter++ , "|" );
			isAtivo	     = ListGetAt( arguments.parametrosBusca , counter++ , "|" );

			q_DadosEstados	= estadoController.listar( id : codigoEstado );
			q_DadosCidades	= cidadeController.listar( id : codigoCidade );

			q_DadosUsuarios = this.instance.dao.getController("Usuarios")
						.buscarUsuario( estado : codigoEstado ,
								cidade : codigoCidade ,
								 ativo : isAtivo );

			loop query="q_DadosUsuarios" {
				structDadosUsuarios[ q_DadosUsuarios.currentRow ] = {
					usuarios : { id : q_DadosUsuarios.id , nome : q_DadosUsuarios.nome ,
						     estadoId : q_DadosUsuarios.estadoId , cidadeId : q_DadosUsuarios.cidadeId } ,
					estado : q_DadosEstados.nome ,
					cidade : q_DadosCidades.nome
				};
			}

			this.instance.usuarios = structDadosUsuarios;

			return this.instance.usuarios;
		}
	</cfscript>
</cfcomponent>

Notamos que modifiquei até o retorno do método de busca, estamos retornando uma struct, contendo os dados individuais dos usuários. Poderíamos ter populado um DTO do usuário e retornado o mesmo, mas a opção de fazer assim, soava melhor para demonstrar a função do ViewHelper.

Nesse exemplo, usamos o ViewHelper para preparar os parâmetros para o Controller e para preparar o retorno contendo os nomes dos Estados e das Cidades.

Para fins de entendimento, vimos que nosso ViewHelper extende de uma classe HelperBase, é o mesmo fundamento das demais camadas, essa base contém métodos que servem para todos os Helpers. Nesse caso a instância da nossa Factory.

Notaremos em nossos desenvolvimentos que na maioria dos casos, precisamos de várias instâncias de vários objetos, e para não termos uma view com um monte de instância, só instanciamos o Helper e este sim, faz as instâncias que precisamos para cada recurso.
Lembrando que essa abordagem que estou fazendo é conceitual, você pode encontrar ViewHelpers que fazem outras coisas.

Bom, fico por aqui, nossa série Implementando uma Arquitetura em Camadas acabou por aqui.
A camada View, por hora vou deixar por sua conta.
Vocês podem ver os demais artigos da série abaixo:

Fico muito contente em terminar essa série e com isso possibilitar que qualquer um consiga estruturar uma aplicação de forma que torna-se muito fácil a manutenção dos códigos.
Adotem algo assim meus amigos, quando as manutenções começam, você notará que tudo ficou bem mais fácil.
Espero que tenha ajudado alguém grande abraço a todos(as) e até a próxima.

Paulo Teixeira

Post original

Permanent link to this article: https://ensina.me/coldfusion/implementando-a-camada-viewhelper/

Leave a Reply