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!

  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: