Arquivos
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!