Abstração de Base de Dados utilizando DAO

6 02 2007

Olá pessoas : )

Bom, ontem me peguei pensando em algo para escrever aqui no blog, e me ocorreu que nunca encontrei muitos textos por ai falando sobre a utilização de DAO com PHP, e que por ser algo que venho usando há algum tempo agora, e que me ajuda bastante, decidi escrever um texto que procura explicar como um DAO funciona, e como pode ser utilizado.

Segue abaixo então, o texto. Em caso de dúvidas, sinta-se a vontade de deixar um comentário ai, ou me escrever : )

Abstração de Base de Dados utilizando DAO
Autor: Henrique <henrique@heap.com.br>
Data: 05/02/2007

Indice
0. Introdução
1. Como funciona?
2. A classe VO (Value Object)
3. A classe DAO (Data Access Object)
4. Utilização
5. Considerações Finais
6. Bibliografia

0. Introdução
Por muitas vezes, o desenvolvedor de sistemas, se depara com um grande
problema quando necessita atualizar a base de dados da sua aplicação, ou
modificar uma query em algum lugar. Quando o sistema fica grande demais, as
queries se perdem em meio ao código, e tudo se torna uma grande bagunça. Há
casos, em que o desenvolvedor precisa migrar o software de um SGDB para outro,
e ai da-lhe correria para encontrar todas as queries e corrigi-las para
funcionar com o novo SGDB.
Para resolver esse tipo de problema, foi criado um padrão, para centralizar as
queries, e permitir que alterando apenas a classe referente a tabela no banco,
possamos ter a aplicação rodando sem problemas.

1. Como funciona?
A idéia por trás do DAO, é bastante simples: transformar as tabelas de nosso
banco de dados em classes VO, e as ações que podemos realizar em classes DAO.
As explicações e exemplos dados nesse texto, irão se basear todos na seguinte
estrutura de base de dados, utilizando MySQL como SGDB de escolha.

create table cliente (

    id_cliente int not null primary key auto_increment,

    nome varchar(30),

    sobrenome varchar(30),

    endereco varchar(50),

    telefone varchar(20)

) engine=INNODB;create table conta (

    id_conta int not null primary key auto_increment,

    id_cliente int,

    numero_conta varchar(12),

    numero_agencia varchar(8),

    tipo_conta varchar(20),

    foreign key(id_cliente) references cliente(id_cliente)

) engine=INNODB;

A partir daqui, iremos criar as classes VO e DAO de cada uma dessas tabelas, e
então demonstrar uma maneira simples de utilização, que pode ser aprimorada,
em combinação com outros patterns, ou até mesmo com a utilização da
imaginação. Ficará então, ao critério do leitor.

2. A classe VO (Value Object)
A classe VO, é a classe que irá fazer a abstração das tabelas de nosso banco
de dados. Ela é uma classe que contém apenas os métodos ‘get’ e ‘set’ para
cada uma das propriedades, que nada mais são que os campos das tabelas.
A classe da tabela cliente então, ficará da seguinte maneira:

<?php

    class ClienteVO {
        /**

        * 

        * @var integer

        */

        private  $id_cliente = NULL;

        /**

        * 

        * @var string

        */

        private  $nome = NULL;

        /**

        * 

        * @var string

        */

        private  $sobrenome = NULL;

        /**

        * 

        * @var string

        */

        private  $endereco = NULL;

        /**

        * 

        * @var string

        */

        private  $telefone = NULL;

        public  function ClienteVO ( ) {

        }

        /**

        * Return the value of "$this->id_cliente"

        * 

        * @return integer

        */

        public  function getId_cliente (  )

        {

            return $this->id_cliente;

        }

        /**

        * Define the value of "$this->id_cliente"

        *

        * @return void

        */

        public  function setId_cliente ( $id_cliente )

        {

            $this->id_cliente = $id_cliente;

        }

        /**

        * Return the value of "$this->nome"

        * 

        * @return string

        */

        public  function getNome (  )

        {

            return $this->nome;

        }

        /**

        * Define the value of "$this->nome"

        *

        * @return void

        */

        public  function setNome ( $nome )

        {

            $this->nome = $nome;

        }

        /**

        * Return the value of "$this->sobrenome"

        * 

        * @return string

        */

        public  function getSobrenome (  )

        {

            return $this->sobrenome;

        }

        /**

        * Define the value of "$this->sobrenome"

        * 

        * @return void

        */

        public  function setSobrenome ( $sobrenome )

        {

            $this->sobrenome = $sobrenome;

        }

        /**

        * Return the value of "$this->endereco"

        * 

        * @return string

        */

        public  function getEndereco (  )

        {

            return $this->endereco;

        }

        /**

        * Define the value of "$this->endereco"

        * 

        * @return void

        */

        public  function setEndereco ( $endereco )

        {

            $this->endereco = $endereco;

        }

        /**

        * Return the value of "$this->telefone"

        *

        * @return string

        */

        public  function getTelefone (  )

        {

            return $this->telefone;

        }

        /**

        * Define the value of "$this->telefone"

        * 

        * @return void

        */

        public  function setTelefone ( $telefone )

        {

            $this->telefone = $telefone;

        }

    }

?>

Deve ser criada uma classe do mesmo tipo para a tabela ‘conta’. Não colocarei
o código aqui, para evitar de prolongar o texto demais com código.
Aconselho ao leitor, criar a classe da tabela ‘conta’ manualmente, para pegar a prática do funcionamento da classe, e memorizar o seu layout.
Agora que as classes estão criadas, vamos partir para a criação das classes
DAO, pois sem elas, as classes VO nos são inúteis.

3. A classe DAO (Data Access Object)
As classes DAO são as responsáveis por realizar o acesso aos dados, em nossa
base de dados. Normalmente, as classes DAO podem ser do tipo Singleton, pois
não a necessidade de se manter mais de uma instancia da mesma, já que os dados
serão todos armazenados nas classes VO. Caso não esteja familiarizado com o
conceito de Singleton, recomendo que dê uma pesquisada no assunto, pois vale
bastante a pena.
Nesse texto, criarei as classes DAO sem utilizar da pattern Singleton, para
facilitar a visualização do funcionamento, para quem não conhece esse pattern.
O DAO que irei demonstrar, terá apenas alguns métodos básicos de busca, e
também um método para inserir registros, um para atualizar e um para apagar.
Você não precisa limitar sua classe DAO a apenas esses métodos, podendo criar
métodos de busca que lhe sejam mais úteis.
Vejamos então:

<?php
    class ClienteDAO {

        /**

         * Método que recebe um objeto do tipo ClienteVO

         * e insere seus valores na base de dados

         *

         * @param $objVo ClienteVO

         */

        public function insert( ClienteVO $objVo )

        {

            $values = array(

                            addslashes( $objVo->getNome( ) ),

                            addslashes( $objVo->getSobrenome( ) ),

                            addslashes( $objVo->getEndereco( ) ),

                            addslashes( $objVo->getTelefone( ) )

                      );

            $sql = sprintf('insert into cliente (nome, sobrenome, endereco,

                            telefone) values( "%s","%s","%s","%s" )',

                            addslashes( $objVo->getNome( ) ),

                            addslashes( $objVo->getSobrenome( ) ),

                            addslashes( $objVo->getEndereco( ) ),

                            addslashes( $objVo->getTelefone( ) )

                            );

            mysql_query( $sql );

            $objVo->setId_cliente( mysql_insert_id( ) );

    

            return $objVo;  

        }

    

        /**

         * Método que retorna todos os registros da tabela

         * em um array de objetos VO

         *

         * @return array Array com objetos VO

         */

        public function getAll( )

        {

        

            $objVo = new ClienteVO( );

            $return = array( );

    

            $sql = 'select * from cliente';

            $resultado = mysql_query( $sql );

            while( $rs = mysql_fetch_array( $resultado ) ) {

            

                $objVo->setId_cliente( stripslashes( $rs['id_cliente'] ) );

                $objVo->setNome( stripslashes( $rs['nome'] ) );

                $objVo->setSobrenome( stripslashes( $rs['sobrenome'] ) );

                $objVo->setEndereco( stripslashes( $rs['endereco'] ) );

                $objVo->setTelefone( stripslashes( $rs['telefone'] ) );

        

                $return[] = clone $objVo;

            }

    

            return $return;

        

        }

        /**

         * Retorna o objVo, referente ao valor de id especificado

         * para o campo de chave primária da tabela

         *

         * @param $id int Valor do campo de chave primária

         * @return ClienteVO Objeto VO com os dados referentes ao registro

         */

        public function getById( $id )

        {

            $objVo = new ClienteVO( );

            $sql = sprintf( 'select * from cliente where id_cliente = "%s"',

                            $id

                          );

            $resultado = mysql_query( $sql );

            while ( $rs = mysql_fetch_array( $resultado ) ) {

                $objVo->setId_cliente( stripslashes( $rs['id_cliente'] ) );

                $objVo->setNome( stripslashes( $rs['nome'] ) );

                $objVo->setSobrenome( stripslashes( $rs['sobrenome'] ) );

                $objVo->setEndereco( stripslashes( $rs['endereco'] ) );

                $objVo->setTelefone( stripslashes( $rs['telefone'] ) );

                

                $return = clone $objVo;

            }

        

            return $return;

        }

    

        public function update( ClienteVO $objVo )

        {

    

            if ( !$objVo->getId_cliente( ) )

                throw new Exception( 'Valor da chave primária inválido' );

            $sql = sprintf('update cliente set nome="%s", sobrenome="%s",

                            endereco="%s", telefone="%s"

                            where id_cliente = "%s" ',

                            addslashes( $objVo->getNome( ) ),

                            addslashes( $objVo->getSobrenome( ) ),

                            addslashes( $objVo->getEndereco( ) ),

                            addslashes( $objVo->getTelefone( ) )

                            );                            

            mysql_query( $sql );

    

        }

    

        public function delete( ClienteVO $objVo )

        {

            if ( $objVo->getId_cliente( ) == null )

                throw new Exception('Valor da chave primária inválido.');

        

            $sql = sprintf('delete from cliente where id_cliente = "%s"',

                            $objVo->getId_cliente( )

                           );

            mysql_query( $sql );

        

        }

    

        public function save( ClienteVO &$objVo )

        {

        

            if ( $objVo->getId_cliente( ) !== null ) {

                $this->update( $objVo );

            } else {

                $this->insert( $objVo );

            }

        }

    }

?>

Nessa classe DAO, voce pode criar por exemplo um método de busca que receba um
objeto VO como parametro, ou um método que receba um nome de campo e um valor,
ou que receba um valor para limit na query, então use a imaginação quando for
criar os métodos que lhe forem ser úteis.
Podemos ver que, as queries estão todas nesse arquivo, então quando existir a
necessidade de utilizar um novo SGDB por exemplo, basta escrever as classes
DAO para esse novo SGDB, e o sistema deverá funcionar normalmente.
A seguir, veremos como utilizar as classes DAO/VO, em um exemplo bastante
simples.

4. Utilização
Tendo os DAOs e VOs criados, podemos utiliza-los de maneiras diversas. No
exemplo que montei acima, utilizei as funções de mysql diretamente no DAO, sem
estabelecer nenhuma conexão, pois assumi que a conexao será inicializada no
começo da execução do script. Caso o leitor deseje, pode criar uma forma de
inicializar a conexão, ao instanciar a classe DAO por exemplo.
Irei criar agora então, um simples formulário para cadastro de clientes, que
faz um submit para a própria página, e utiliza método POST.

<?phpif( isset($_POST['enviar']) ) {

    // Conectar na base de dados

    mysql_connect('localhost','root','');

    mysql_select_db('daoTutorial');

    // Incluir classe VO

    include_once('clientevo.class.php');

    // Incluir classe DAO

    include_once('clientedao.class.php');

    // Instanciamos o objeto VO, e o preenchemos

    $cliente = new ClienteVO( );

    $cliente->setNome( $_POST['nome'] );

    $cliente->setSobrenome( $_POST['sobrenome'] );

    $cliente->setTelefone( $_POST['telefone'] );

    $cliente->setEndereco( $_POST['endereco'] );

    $clienteDAO = new ClienteDAO( );

    $cliente = $clienteDAO->insert( $cliente );

    printf('Registro inserido com sucesso. O id do cliente é %s<br><br>', $cliente->getId_cliente( ));

}

?>

<html>

    <head>

        <title>Exemplo uso de DAO</title>

    </head>

    <body>

        <form method='POST'>

            Nome: <input type='text' name='nome'><br>

            Sobrenome: <input type='text' name='sobrenome'><br>

            Endereço <input type='text' name='endereco'><br>

            Telefone  <input type='text' name='telefone'><br>

            <input type='submit' value='Enviar' name='enviar'>

        </form>

    </body>

</html>

Acessando essa pagina, um formulário simples será apresentado. Quando
preenchido e submetido, o php irá checar se o botão enviar foi realmente
clicado, e caso sim, o objeto VO será preenchido com as informações que foram
passadas no formulário, e fará com que a classe DAO insira esses dados na base
de dados. O método insert da classe DAO retorna o objeto preenchido com os
dados, mais ovalor que recebeu como chave primária.
Para testar esse exemplo, não esqueça de colocar a classe VO e a classe DAO no
mesmo diretorio que se encontra o script, e arrumar as informações de conexão
com a base de dados.
Se deseja listar todos os registros da base de dados, pode criar um script de
listagem, com código semelhante ao que segue:

<?php    // Conectar na base de dados

    mysql_connect('localhost','root','');

    mysql_select_db('daoTutorial');

    // Incluir classe VO

    include_once('clientevo.class.php');

    // Incluir classe DAO

    include_once('clientedao.class.php');

    $clienteDAO = new ClienteDAO( );

    // pega todos os clientes

    $clientes = $clienteDAO->getAll( );

    if( sizeof($clientes) > 0 ) { // verifica se há algum cliente cadastrado

        foreach( $clientes as $objVo ) {

            printf('Nome: %s <br>',$objVo->getNome( ));

            printf('Endereço: %s <br>', $objVo->getEndereco( ));

            echo '<hr><br>';

        }

    }

?>

Os métodos de update e delete, exigem que o campo de chave primária esteja
preenchido para realizarem a sua ação, então fique atento a isso.

5. Considerações finais
Os exemplos apresentados, não dão muita importancia a segurança, embora
apresentem algumas boas práticas de programação, como procurar comentar o
código. Percebam que uma medida de segurança é tomada, ao usar o comando
addslashes nas queries SQL, para evitar injection, mas isso apenas pode não
ser o bastante. Uma filtragem dos dados mais bem feita com certeza se faz
necessária. Veja também que no método de update, não é feita nenhuma
verificação, para saber se o registro realmente existe na base de dados, antes
de tentar atualizar, o que pode causar problemas.
A pattern DAO pode ser combinada com outras patterns, para desenvolvimento de
sistemas de todos os portes, e é bastante interessante pois concentra toda a
relação com o banco de dados em um só lugar, e permite que o filtro das
informações que são enviadas, possa ser feito nesse mesmo lugar, facilitando a
tomada de medidas de segurança no desenvolvimento.

6. Bibliografia
Não consigo me lembrar exatamente que textos li para me aprofundar nesse
assunto, nem quais sites visitei, já que há algum tempo que utilizo isso. Devo
agradecer ao Carlos (que provavelmente nem vai ler esse texto), pois ele me
apresentou o conceito de DAO. Talvez possa encontrar algo no google, que possa
ter informações interessantes. Encontrei um texto no site da IBM,que parece
interessante:

http://www-128.ibm.com/developerworks/java/library/j-dao/

E um ‘texto’ bem esdruxulo da wikipedia

http://en.wikipedia.org/wiki/Data_Access_Object

About these ads

Ações

Information

15 responses

9 02 2007
Joel

Muito legal vocês estarem contribuindo com o crescimento de muitos outros programadores.

Eu queria somente discutir um trecho de codigo que esta sendo mostrado neste post:

public function save( ClienteVO &$objVo ) {

if ( $objVo->getId_cliente( ) !== null ) {
$this->update( $objVo );
} else {
$this->update( $objVo );
}
}

Acredito que ele contenha erro, do tipo que esta sendo testado se o cliente existe, porem, em qualquer uma das situações ele tenta atualizar e acredito que possa causar um erro de UPDATE que esta dentro do else.

Pelo que notei no codigo a cima, neste local deveria estar o insert.

Bom essa foi uma obs que fiz no codigo.

Um grande abraço a todos e continuem postantando conteudos e replicas que assim evoluiremos.

9 02 2007
typoon

Hehehehe, verdade. Eu nem reparei que coloquei update duas vezes. Vou corrigir : )
Valeu ai pela observação!

10 10 2007
Alex

Tbm achei legal seu artigo tratando de padrões em php, pois ainda é dificil encontrar bons materiais a respeito utilizando a linguagem.

Gostaria de ver o exemplo das classes DAOs no padrão singleton

Vlw

16 10 2007
Carlos

Cara, muito bom o seu artigo, ajudou muito na minha formação.
Valeu!

13 12 2007
Tiago Hillebrandt

Boa tarde,

Ótimo artigo, porém só não entendi uma coisa: pra quê serve aquele vetor na linha 18 do ClienteDAO? No mais ficou show (:

Até mais!

9 05 2008
Leandro Fernandes

Muito bom o artigo, estava a procura de informações sobre como criar abstração de acesso a dados e tive sorte de encontrar seu artigo, mas o que fiquei interessado mesmo é se existe algum software que ja faz este serviço de gerar as classes porque é uma trabalheira daquelas, sendo que é um trabalho bem repetitivo e genérico.

abraços.

14 11 2008
Edinei

Amigo pode dar um exemplo onde as querys utilizam left joins ou inner joins.

Aguardo uma resposta.

Ps. Muito bom o artigo, me ajudou muito.

5 01 2009
manuela

ei cara …. mui bueno tu artigo….

valeu pla ajuda

kiss on your face

26 10 2009
Vinícius

Conceitualmente a idéia é muito show mas tenho alguns pontos a discutir, se eu estiver errado me avisem por favor:

1 – No método getAll() da classe ClienteDAO, você consulta o banco e faz um vetor de objetos
ClienteVO e retorna este vetor, para isso você usa o “while( $rs = mysql_fetch_array( $resultado ) )”, até ai tudo ok.
Mas veja no método getById(), você também faz um loop mas não retornaria sempre 1 registro? Precisa do loop?

2 – Quando eu quiser buscar todos os clientes, utilizaria o getAll() da classe ClienteDAO, mas quase sempre precisamos
exibir quantos registros tem no banco. O que quero mostrar é a quantidade de “fors ou whiles” vamos analisar:

A – Se eu fizer isso diretamente com métodos do próprio PHP(mysqli) vou usar apenas 1 loop, pois vou executar a query
e depois dar $query->fetch_object() até acabar os registros, e para saber a quantidade de registros eu uso o atributo
$query->num_rows;

B- O método getAll() faz 1 loop para jogar os objetos(ClienteVO) no vetor e depois retorna o vetor, quando for exibir
isso na tela vou fazer mais um loop, e quando quiser saber a quantidade vou usar sizeof($vetor_objetos). Ou seja são
dois loops e uma chamada a função sizeof() que conta quantos elementos tem na array. É um processamento alto desperdiçado
principalmente se a quantidade de registros for grande.

Aguardo comentários.

24 03 2010
Belmiro

Adoro portuguê

24 03 2010
Belmiro

Belmiro @heap.com.br data 15.03.2010

17 05 2010
Lucas Martins

As validações e formatações de dados entram na DAO ou no Controller?

14 10 2010
Renato Miawaki

Ola, Achei seu blog pelo google pois estava me perguntando porque que tão poucas pessoas utilizam DAO e VO em PHP e é muito comum em JAVA.

E achei muito legal saber que tem mais gente usando e entendendo.

Claro que essa é uma explicação por cima com um pequeno exemplo, mas eu acrescentaria uma classe DAO que já conecta com banco de dados na qual a ClienteDAO extende ficando algo como
class ClienteDAO extends DbInterface{}
Assim no exemplo não precisaria fazer a conexão com o banco no script.

E na ClienteVO para facilitar a vida podria ter um metodo que já faz o trabalho de popular a vo, algo como

public funciton setFetchArray($fetch_array){
$this->setId_cliente( stripslashes( $fetch_array['id_cliente'] ) );
$this->setNome( stripslashes( $fetch_array['nome'] ) );
$this->setSobrenome( stripslashes( $fetch_array['sobrenome'] ) );
$this->setEndereco( stripslashes( $fetch_array['endereco'] ) );
$this->setTelefone( stripslashes( $fetch_array['telefone'] )
}

E isso facilitaria bastante a utilização.

E a colocação de outros comentários também são pertinentes, mas claro que devemos entender que é apenas um exemplo.

Renato

12 05 2011
Bruno Freixo

Excelente iniciativa amigo!
É muito difícil achar bons materiais, principalmente na internet, que tratam de assuntos tão relevantes no PHP Orientado a Objetos como os design patterns.

Sugiro uma matéria sobre padrões de projeto em PHP utilizando Factory e Singleton.

Parabéns,
abraços!

14 11 2012
Augusto

Ola tudo bem! venho pesquisando sobre esse padrão de projeto e achei bem interessante, a minha duvida é em relação a outras classes, ex: carrinho de compras, paginação, etc como seriam organizadas nesses padroes de projeto, como seria um carrinho de compra com dao e vo

Deixe um comentário

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




Seguir

Obtenha todo post novo entregue na sua caixa de entrada.

Junte-se a 27 outros seguidores

%d blogueiros gostam disto: