zf2: Preparando para diferentes banco de dados em backend.

Este artigo é uma tradução mal feita do artigo Preparing for different Database-Backends da documentação do Zend Framework 2.4 feita por mim mesmo (Vasconcelos) para minhas consultas futuras, é claro que estou deixando disponível para qualquer pessoal que necessitar.

No capítulo anterior nós criamos um Postservice que retorna alguns dados de postagens do blog. Enquanto isso serviu para facilitar a aprendizagem isto é bastante impraticável para aplicações do mundo real.
Ninguém iria querer modificar os arquivos fontes a cada vez que uma nova mensagem fosse adicionada. Mas felizmente todos nós sabemos sobre bancos de dados. Tudo o que precisamos saber é aprender a interagir com banco de dados de nossa aplicação ZF2.

Mas há um apanhador. Existem muitos sistemas de banco de dados back-end, especialmente bancos de dados SQL e NoSQL. Em um mundo real você provavelmente iria pular direto para a solução que se encaixa melhor no momento, uma prática melhor é criar outra camada abstrata em frente a verdadeira base de dados que interage com o banco de dados. Nós a chamaremos de Mapper-Layer (camada mapa).

O que é abstração de banco de dados?

O termo “abstração de banco de dados” pode parecer um pouco confuso, mas realmente é uma coisa bem simples. Considere um banco de dados SQL e um NoSQL. Ambos têm possibilidade para operações CRUD (criar, ler, atualizar, excluir). Por exemplo, para consultar o banco de dados a partir do MySQL você faria uma terminada linha mysqli_query('SELECT foo FROM bar'). Mas usando um ORM para MongoDB por exemplo, você faria algo como: $mongoODM->getRepository('bar')->find('foo'). Ambos os mecanismos lhe daria o mesmo resultado mas com execução é diferente.

Então, se começarmos usando um banco de dados SQL e escrevermos esses códigos diretamente dentro de nossa PostService e um ano mais tarde, decidirmos mudar para um banco de dados NoSQL, iríamos literalmente ter que apagar todas as linhas previamente codificadas e escrever novas linhas. E em poucos anos mais tarde quando uma coisa nova aparece nós temos que apagar os códigos reescrever novamente. Isto realmente não é a melhor proposta abordada onde a abstração de banco de dados ou Camada mapeamento vem a calhar.

Basicamente o que nós temos que fazer é crair uma Interface.
Esta interface então define como a nossa interção com banco de dados deverá funcionar mas a implementação real deve ficar de fora. Mas vamos parar com a teoria e vamos codificar isto.

Criando a PostMapperInterface

Vamos primeiro pensar um pouco sobre possibilidade de interações com banco de dados, nós podemos pensar quê. Nós precisamos ser capazes de:

  • encontrar um único do post
  • encontrar todos os posts no blog
  • inserir novo post
  • atualizar posts existentes no blogs
  • apagar posts existentes no blog

Eu acho que estas são as mais importantes por hora. Considerando que insert() e update() ambos escrevem no banco de dados seria bom ter uma única função save() que internamente chame a função adequada.

Comece criando um novo arquivo chamado PostMapperInterface.php dentro de um novo namespace chamado Blog\Mapper e adicione o seguinte contúdo a este.


 namespace Blog\Mapper;
// Filename: /module/Blog/src/Blog/Mapper/PostMapperInterface.php

use Blog\Model\PostInterface;

interface PostMapperInterface
{
/**
* @param int|string $id
* @return PostInterface
* @throws \InvalidArgumentException
*/
public function find($id);

/**
* @return array|PostInterface[]
*/
public function findAll();
}

Como você pode ver nós definimos duas funções diferentes. Dizemos que uma implementação de mapeamento supostamente terá uma função find() que retorna uma simples implementação do objeto PostInterface. Então, nós precisamos ter uma função chamada findAll() que retorna uma matriz de objetos da aplicação do PostInterface. Possíveis definições para save() e delete() não serão adicionadas a Interface ainda uma vez que estamos olhando apenas para o lado da leitura das coisas agora. Embora elas sejam adicionadas em um ponto mais tarde.

Refatorando a PostService

Agora que nós definimos como o nosso mapeamento irá agir, podemos fazer uso dele dentro de nossa PostService. Para começar o processo de refatoração vamos esvaziar nossa classe e apagar todo o conteúdo atual. Em seguida implemente as funções que definimos no PostServiceInterface e você deve ter uma PostService vazia parecida com esta

A primeira coisa que precisamos ter em mente é que essa interface não está implementada na nossa PostService mas é bastante utilizada como uma dependência. Uma dependência é necessária, portanto, precisamos criar uma __construct() que pega qualquer implementação desta interface como um parâmetro. Além disto você deverá criar uma variável protegida para armazenar o parâmetro.


postMapper = $postMapper;
    }

    /**
    * {@inheritDoc}
    */
    public function findAllPosts()
    {
    }

    /**
    * {@inheritDoc}
    */
    public function findPost($id)
    {
    }
}

Com isso agora nós exigimos uma implementação do PostMapperInterface para o nosso PostService para funcionar. Uma vez que ainda não existe nenhuma, nós podemos não podemos pôr o nosso aplicativo para trabalhar e nós estaremos vendo o seguinte erro PHP:

Mas o que podemos fazer está dentro das premissas que podemos fazer. Este PostService sempre terá um mapeador passado como um argumento. Assim em nossas funções find*() nós podemos supor que ele está lá. Lembre-se que a PostMapperInterface define uma função find($id) e uma findAll(). Vamos usá-las dentro de nossas funções Service:

Olhando para este código, você verá que nós usamos o postMapper para obter acesso aos dados que queremos. Como isso está acontecendo não é mais negócio da PostService. Mas a PostService sabe quais os dados que irá receber e isso é a única coisa importante.

O PostService tem uma dependência

Agora que nós introduzimos o PostMapperInterface como uma dependência para o PostService nós não estamos haptos a definir este Service como um Invokable porque isto tem uma dependência. Então precisamos criar uma factory (fábrica) para o serviço. Faça isso criando uma factory da mesma forma que fizemos para a ListController. Primeiro mude as configurações da entrada em invokables para factory e atribua a classe factory adequada:


 array(
		'factories' => array(
			'Blog\Service\PostServiceInterface' => 'Blog\Factory\PostServiceFactory'
		)
	),
	'view_manager' => array( /** ViewManager Config */ ),
	'controllers'  => array( /** ControllerManager Config */ ),
	'router'       => array( /** Router Config */ )
);

Passando pela configuração acima agora precisamos criar a classe Blog\Factory\PostServiceFactory então vamos em frente e criá-lo:


get('Blog\Mapper\PostMapperInterface')
		);
	}
}

Com isto que você deve agora ser capaz de ver o ServiceNotFoundException, lançada pelo ServiceManager, dizendo que o serviço solicitado não pode ser encontrado.

Additional information:
Zend\ServiceManager\Exception\ServiceNotFoundException
File:
{libraryPath}\Zend\ServiceManager\ServiceManager.php:529
Message:
Zend\ServiceManager\ServiceManager::get was unable to fetch or create an instance for Blog\Mapper\PostMapperInterface

Conclusão

Finalizamos este capítulo com o fato de que houve sucesso em manter a lógica de banco de dados fora do nosso serviço. Agora somos capazes de implementar soluções de banco de dados diferentes dependendo da nossa necessidade e alterá-las facilmente conforme o tempo exige.

No próximo capítulo, vamos criar a implementação real da nossa PostMapperInterface usando Zend\Db\Sql.

Deixe uma resposta

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 )

Imagem do Twitter

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

Foto do Facebook

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

Foto do Google+

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

Conectando a %s

%d blogueiros gostam disto: