Início > Design Patterns > Implementando Data Access Object em PHP

Implementando Data Access Object em PHP

O padrão de projetos Data Access Object define uma forma de encapsular o acesso a base de dados de uma aplicação.
O padrão DAO faz parte da implementação de softwares baseados no modelo MVC.

O problema:

<html>
	<header>
		<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />	
	</header>
	<body>
<?php
	//criando a conexao
	$conexao = @mysql_connect( 'root', 's3nH@','localhost', "db" ); or die( "Erro escroto" );
	$query = mysql_query( $conexao, "SELECT * FROM pessoa" );
	
	if( !$query ){
		echo '<h1>Erro na aplicação, contate o suporte</h1>';
		exit;
	}
?>
		<table width=100% cellpading=0 cellspacing=0>
			<?php while ($row = mysql_fetch_array($res)) { ?>
			<tr>
				<td><?php echo $row['id'];?></td>

				<td><?php echo $row['nome'];?></td>

				<td><?php echo $row['sobrenome'];?></td>

				<td><?php echo $row['data_nascimento'];?></td>

				<td><?php if( $row[ 'sexo' ] == 'M' ) echo 'Masculino' else echo 'Feminino'; ?></td>
			</tr>
			<?php } ?>
		</table>
	</body>
</html>

Ex. 1

Nesse caso estamos obtendo dados de um banco MySQL.
Imagine então que varias páginas do nosso sistema buscassem dados dessa mesma tabela do banco de dados:
Em cada tela eu teria de criar uma conexão, criar uma consulta SQL e imprimir os dados na tela… O que tem de errado?
Agora imagine se voce tivesse que, por algum motivo qualquer, mudar o banco de dados (Agora usando PostGre)…
Um desastre! Teriamos de editar varios arquivos, repetir o mesmo código em varios lugares e teria um tremendo retrabalho.

Para evitar esse problema, uma boa prática e usar o padrao DAO – Data Access Object – que entre outras utilidades modela uma forma melhor de obter acesso a base de dados, encapsulando todas as regras de acesso a persistência.

Digamos que a tabela pessoa na base de dados seja assim:

Nome Sobrenome
Joao Neto
Maria Silva

Precisamos então de uma classe que mapeie essa tabela do banco que e justamente o objeto de transferencia entre minha aplicaçao e a base de dados.
Transformando a tabela em objeto teremos:

<?php
class PessoaTO
{
	private $nome;
	
	private $sobrenome;
	
	public function getNome()
	{
		return $this->nome;
	}
	
	public function setNome( $nome )
	{
		$this->nome = $nome;
	}
		
	public function getSobrenome()
	{
		return $this->sobrenome;
	}
	
	public function setSobrenome( $sobrenome )
	{
		$this->sobrenome = $sobrenome;
	}
}
?>

Ex. 2

Agora implementando a classe DAO:

<?php
class PessoaDAO
{
	/**
	 * @var Db();
	 */
	private static $db;
	
	public function __construct()
	{
		if( !$this->db )
		{
			$this->db = new Db();
		}
	}
	
	/**
	 * Get all Objects from Database
	 * @return array|false Array of PessoaTO Objects or false in error or empty query
	 */
	public function findAll()
	{
		$sql = "SELECT nome, sobrenome FROM pessoa";
		
		$rows = $this->db->fetchAll( $sql );
		
		if( !$rows )
		{
			return false;
		}
		
		foreach( $rows as $row )
		{
			$pessoas[] = PessoaFactory::create( $row );
		}
		
		return $pessoas;
	}
	
	/**
	 * Get all Objects from Database whose name is equal to the param
	 * @param string $nome param for the WHERE clausure
	 * @return array|false Array of PessoaTO Objects or false in error or empty query
	 */
	public function findByNome( $nome )
	{
		$sql = "SELECT nome, sobrenome FROM pessoa WHERE nome = '{$nome}'";
		
		$rows = $this->db->fetchAll( $sql );
		
		if( !$rows )
		{
			return false;
		}
		
		foreach( $rows as $row )
		{
			$pessoas[] = PessoaFactory::create( $row );
		}
		
		return $pessoas;
	}
}
?>

Ex. 3

É na classes DAO que estarão os métodos de acesso a minha tabela do banco.
Eu posso buscar “Todas as pessoas cujo nome e ‘joao'” ou buscar as pessoas por ordem alfabética.
A classe DAO é responsavel pelos métodos find(), delete(), store() e update() de cada objeto do banco. Para cada objeto (diga-se entidade) será criado um DAO correspondente.

A classe Db:

<?php
class Db
{
	private static $connection;
	
	private static final $user = "user";
	
	private static final $pass = "p@55";
	
	private static final $host = "localhost";
	
	private static final $db = "dbname";
	
	public function __construct()
	{
		if( !$this->conection )
		{
			$this->conection = $this->startConnection();
		}
	}
	
	/**
	 * Execute a Query to database
	 * Use this method to execute UPDATE, INSERT, DELETE and DROP commands
	 * @param string $query SQL command to execute
	 * @return int|boolean Number of affected rows, success or false in error.
	 */
	public function query( $query )
	{
		$result = mysql_query( $query, $this->connection );
		return $result;
	}

	/**
	 * Fetch all rows to database
	 * @param string $query SQL command to execute
	 * @return array|boolean Array of returned rows or false in error or empty set
	 */
	public function fetchAll( $query )
	{
		$result = mysql_fetch_array( $query, $this->connection );
		
		if( !$result )
		{
			return false;
		}
		
	}

	/**
	 * Fetch one row to database
	 * @param string $query SQL command to execute
	 * @return array|boolean Array of returned row or false in error or empty set
	 */
	public function fetchOne( $query )
	{
		$result = mysql_fetch_row( $query, $this->connection );
		
		if( !$result )
		{
			return false;
		}
		
		return $result;
	}
	
	/**
	 * Private function to start a connection to database
	 * {@code}
	 * @return resource connection to database
	 */
	private function startConnection()
	{
		$conection = @mysql_connect( $this->user, $this->pass, $this->host );
		mysql_select_db( $this->db, $conection );
		return $conection;
	}
}
?>

Ex. 4

A classe Db foi criada para abstrair o acesso aos comandos SQL do banco. E uma boa forma de acoplar ainda mais o acesso a base de dados, ja que estou deixando em um so lugar os metodos comuns da conexao com a base de dados.

<?php
class PessoaFatory
{
	/**
	 * Map for the Database Column and Class method
	 * <<database column>> => attribute setter method in Transfer Object
	 */
	private static objectMap = array(
		'nome' => 'nome';
		'sobrenome' => 'sobrenome';
	);
	
	public static function get( $arr )
	{
		$pessoa = new PessoaTO();
		
		foreach( self::objectMap as $key => $setter )
		{
			$method = 'set' . ucfirst( $setter ); //ex: setNome
			$pessoa->{$method}( $arr[ $key ] );
		}
		
		return $pessoa;
	}
	
	public static function extract( PessoaTO $pessoa )
	{
		foreach( self::objectMap as $key => $setter )
		{
			$method = 'get' . ucfirst( $setter ); //ex: getNome
			$arr[ $key ] = $pessoa->{$method}();
		}
		
		return $arr;
	}
}
?>

Ex. 5

Por último usamos um outro padrão chamado Abstract Factory para fabricar um objeto apartir do resultado da consulta.

No Factory é onde a minha aplicação vai pegar “qualquer coisa” que vem da base de dados e transformar em um objeto da minha aplicação.

No nosso caso esse “qualquer coisa” é uma linha da tabela pessoa, mas poderia ser um arquivo de Texto, XML ou uma base de dados objeto-relacional, etc…

O atributo “objectMap” mapeia a tabela do banco => atributo do objeto. Uma dica pra facilitar seu trabalho!

O método get() transforma o array em objeto e o método extract() transforma o objeto em array…

Algumas referências interessantes:
Data Access Object Pattern: http://java.sun.com/blueprints/corej2eepatterns/Patterns/DataAccessObject.html
Abstract Factory Pattern: http://msdn.microsoft.com/en-us/library/ms954600.aspx

Espero ter ajudado.

Um abraço!

Anúncios
  1. Nenhum comentário ainda.
  1. No trackbacks yet.

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: