Ir para conteúdo

POWERED BY:

Arquivado

Este tópico foi arquivado e está fechado para novas respostas.

ronaldoalmeida

myframework

Recommended Posts

interessante, você disse que o model cuida da logica, e o seu model tem apenas metodos para armazenagem dos dados, eu nao vi metodos que eu poderia usar para modificar algumas regras, como por exemplo, inputar um juros em sistema de vendas...poderia explicar um pouco?assim acaba virando um simples DAO (Data Access Object - objeto de acesso a dados) e nao um Model (modelo), se o model cuida da logica, portanto, das regras de negocios...ja o bean ou valueobject cuida da entrada, saida e validacao dos dados, assim como o DAO cuida de sua gravacao e leitura de dados...

 

Eu não coloquei lógica do negócio no Model para não tornar o exemplo muito extenso, mas preste atenção ali na inserção. Antes de inserir, é necessário verificar se o usuário já existe na base de dados, ou seja, já tem um pouco da lógica do negócio aí.

Não coloquei no código, mas coloquei no diagrama: a classe ProductModel tem um método chamado 'putOnDiscount', que também faz parte da lógica do negócio, pois não se restringe apenas à alterar um campo no banco de dados.

 

Henrique

Gostei bastante do seu código, vou pegar algumas idéias, e implementar em meu novo sistema. Porém, me tira uma dúvida que bateu.

 

Porque você tá usando interface, classes abstratas e esse tanto de classes?

Eu quero realmene entender o motivo disso tudo, principalmente a inteface e classes abstratas, quando usar e por que usar.

 

Você conhece UML? Deu uma olhada no diagrama que eu postei?

 

As interfaces definem o comportamento de um certo conjunto de objetos, não necessariamente com algum relacionamento de herança. Uma interface define O QUE um objeto deve fazer, mas não COMO. Quando uma classe implementa uma interface, você deve garantir que ela implemente todos os métodos definidos na interface, a menos que a classe seja abstrata, aí você pode postergar essa implementação para as classes.

 

Uma classe abstrata serve para definir operações em comum para suas classes derivadas. É possível também declarar métodos abstratos, que devem ser implementados por suas classes filhas. Nesse aspecto, elas funcionam também como uma interface.

 

No meu exemplo acima, DataAcessObject é uma inteface que define O QUE deve ser feito para garantir uma camada de acesso a dados funcional. Como isso vai ser implementado, até agora não importa. Você pode utilizar bancos de dados relacionais, orientados a objeto, arquivos de texto, arquivos XML, feeds RSS, etc. Tudo isso você define depois, quando for passar um objeto que implementa DataAccessObject para o construtor do Model:

abstract class ApplicationModel {
   protected $_dao;    
   public function __construct(DataAccessObject $dao){
       $this->_dao = $dao;
   }
}

 

"E como decidir se eu devo utilizar interfaces ou classes abstratas?"

Não existe uma regra, no sentido literal da palavra, mas guie-se por isso aqui:

Utilize interfaces para definir um comportamento comum a vários tipos de objetos, não necessariamente relacionados entre si.

Utilize classes abstratas quando houver uma implementação comum de um comportamento para classes relacionadas por herança.

 

Um exemplo bacana de interface de uso geral é a Serializable. Ela define um comportamento comum a todos os objetos que possam ser convertidos em um texto serializado ("codificado") e reconstruídos a partir desse mesmo texto.

Note que não há necessidade de um relacionamento entre as classes que vão implementar essa interface.

 

Capiche?

 

Quanto ao seu código:

abstract class CRUD{

       protected function insert();
       protected function update();
       protected function delete();
       protected function getAll(){
          $query = "SELECT * FROM clientes";
       }
       protected function getOne();
}

class Usuario extends CRUD{

       public function getAll(){
               return parent::getAll();        
       }
}

 

Tem um problema: sua classe CRUD só serve para tratar clientes.

Além disso, é impossível chamar os métodos de fora da classe.

Você não vai poder fazer:

$usuario = new Usuario();
$usuario->insert();

 

Que tal fazer dessa maneira:

interface CRUD{
       public function insert();
       public function update();
       public function delete();
       public function getAll();
       public function getOne($id);
}

class Usuario extends CRUD{

       public function getAll(){
               $query = 'SELECT * FROM clientes';
               //Agora execute a query...
       }
}

 

Você pode definir e executar a conexão e operações com o banco de dados dentro das classes CRUD, funciona. Mas e se um dia você não for utilizar mais o seu SGBD tradicional? Você vai ter que entrar em todas as classes uma a uma e alterar tudo o que diz respeito a essas operações.

 

Utilizando o padrão Adapter, como no meu exemplo, esse problema praticamente deixa de existir. As únicas alterações que você terá que fazer seria alterar a instancia do Adapter passado ao CRUD.

Compartilhar este post


Link para o post
Compartilhar em outros sites

Cara, eu vouler, reler e reler até eu entender 100% o que você disse.

Explicou muito bem, porém ainda não entrou na minha cabeça.

 

O nível de raciocínio de OO é "estranho", sei la, bem diferente.

Bom, agradeço muito mesmo pela paciência em explicar, vou dar umas estudadas!

 

Eu vou ler e entender sobre este padrão Adapter, e fazer o meu, claro olhando seu exemplo, porém NÃO vou pegar seu código, eu penso que quem se preze pelo menos um programador iniciante, deve rachar a cuca e fazer seu algoritmo, mas é claro que tem coisas que não vou reinventar a roda! hehehe

Compartilhar este post


Link para o post
Compartilhar em outros sites

É assim mesmo cara, ler sobre programação não é como ler uma poesia, uma reportagem, etc. Você nunca gosta de primeira, porque nunca entende de primeira.

 

Você lê uma, duas, dez vezes. Seu cérebro fica trabalhando a idéia em background, até que um dia a ficha cai e, normalmente, não é durante a leitura :P hehe...

Compartilhar este post


Link para o post
Compartilhar em outros sites

É assim mesmo cara, ler sobre programação não é como ler uma poesia, uma reportagem, etc. Você nunca gosta de primeira, porque nunca entende de primeira.

 

Você lê uma, duas, dez vezes. Seu cérebro fica trabalhando a idéia em background, até que um dia a ficha cai e, normalmente, não é durante a leitura :P hehe...

 

É, concordo 100% contigo. o jeito é eu ir lendo mais sobre o assunto, e botar mão na massa, por que é obvio que só ler não vai resolver todos os meus problemas. Orientação a Objetos é complexo, porém é bem divertido, o difícil que é legal, e o que dá dinheiro! hehehhe.

 

Eu vendo seus códigos e do João Bastista, ajuda bastante.

 

Só mais uma pergunta(ou duas): É essencial saber Design Pattern quando quer realmente aplicar OOP?

 

OOP necessariamente TEM que implementar algum DP?

Compartilhar este post


Link para o post
Compartilhar em outros sites
Só mais uma pergunta(ou duas): É essencial saber Design Pattern quando quer realmente aplicar OOP?

Acaba surgindo essa necessidade. Tanto OOP quanto DP dizem respeito a reuso inteligente de código.

 

OOP necessariamente TEM que implementar algum DP?

Necessariamente não, mas fica tão natural utilizar um Design Pattern onde ele foi feito para ser usado, que não faz sentido fazer do jeito 'errado'.

 

Nem todos os Design Patterns dizem respeito a objetos, mas a grande maioria o faz, devido à própria natureza do código orientado a objetos. Pense em objetos como 'peças' e os design patterns como o 'melhor encaixe' entre essas peças.

 

Só gostaria que o autor do tópico desse seu veredito para fecharmos o tópico...

Compartilhar este post


Link para o post
Compartilhar em outros sites

Certo. Agradeço novamente por me tirar essas dúvidas, no mais, é estudar!

Compartilhar este post


Link para o post
Compartilhar em outros sites

Peguei seu código e estou estudando nele! Puuuts, tá bacana esse negócio, eheheheh.

Obrigado por compartilhar.

 

Depois de analisar bem seu código, eu sinto que sei absolutamente NADA de OOP :(

 

Ao meu ver, ficou sensacional esse seu esquema, muito bom mesmo! Eu não quero de maneira alguma copiar teu código, mas como tu tá usando o Adapter, será meio difícil reinventar a roda!

Compartilhar este post


Link para o post
Compartilhar em outros sites

:natallaugh: Eu consegui captar essa ideia dando uma estudada nos códigos-fonte de alguns frameworks. Os exemplos que encontramos por aí em livros e na internet mesmo são meio "abstratos" demais, não se aplicam na prática. Você tem que ter um poder de abstração muito grande pra conseguir tirar alguma utilidade para aquilo.

 

Faz mais de ano que estudo sobre padrões e só agora as coisas estão começando a clarear um pouco... E não tem jeito, tem que ser na marra mesmo...

Compartilhar este post


Link para o post
Compartilhar em outros sites

Já li vários exemplos de livros, e eu também não entendi tão bem, quanto estou entendendo o seu código.

Esse negócio de padrões parece ser meio difícil mesmo, porém parece ser necessario, depois que entende sempre fica mais fácil!

Compartilhar este post


Link para o post
Compartilhar em outros sites

Agora me fala a utilidade do exemplo dado...

Esse é o problema... Elas descrevem problemas abstratos demais, que não se aplicam a nossa realidade...

Compartilhar este post


Link para o post
Compartilhar em outros sites

Essa é a grande e infeliz realidade da programação intermediário-avançada no Brasil.

 

Em termos relativos, poucas pessoas sabem MESMO programar Orientado a Objetos

 

Desses poucos, alguns menos se dão ao trabalho de compartilhar o conhecimento adquirido, seja por experiência ou por ter aprendido com artigos/cursos estrangeiros.

 

Porém nem todo mundo sabe ensinar. Ensinar é um dom, não muito diferente de fazer uma torta. Não basta você demonstrar o que é e pra que serve e exemplificar com exemplo vazios.

 

Quem vai conseguir enxergar além dessa abstração são aqueles que não precisam ler o seu artigo, pois já sabem o que o Strategy é, o que faz e onde pode ser aplicado no mundo REAL.

 

Agora quem precisa aprender para elevar o conhecimento por aprender a programar CERTO, vai simplesmente se perder ou terá tamanha dificuldade em entender que achará (e com certa razão) que todos os Design Patterns são complicados que nem, e vão simplesmente ignorá-los.

 

O resultado é essa profliferação de "sobrinhos" discutida no passado recente, em outro tópico.

 

Triste :natalsad:

Compartilhar este post


Link para o post
Compartilhar em outros sites

Eu penso que: o profissional só consegue realmente programar orientado a objetos com muita prática durante anos, ninguém aprende o mais difícil do dia para o outro, isso é fato!

 

Por exemplo, o admin deste fórum, o João Batista Neto certamente ralou DEMAIS pra ter o conhecimento que tem, não só ele, mas você, entre outros daqui. Não se pode queixar-se por não conseguir programar igual outra pessoa, ou por não conseguir fazer algo que realmente quer, por que isso vai com o tempo de experiência, e cada um faz seu código de acordo com seus conhecimentos.

 

Eu por exemplo, estou aprendendo OOP já deve fazer pouco menos de 1 ano, e ainda tá uma porcaria, mas é assim, só com o tempo vou melhorando meus códigos, cada dia que passa tenho sempre novas idéias e vontade de melhorar, por que ainda me incomoda!

Compartilhar este post


Link para o post
Compartilhar em outros sites

É muito interessante essa questão da dificuldade de aprender e aplicar de maneira correta os diferentes design pattern.

 

Eu ainda estou estudando... e muito (5 horas por dia) designer pattern. Mas Lembro que no inicio, quando estava iniciando na programação OOP, eu pensava em começar criando diagrama de classes (UML) e só depois de definir esses diagramas devia partir para a programação...

 

Bom, a minha experiência seguindo essa linha (UML -> Design pattern -> Programação) foi terrível... comecei a não sair nem do UML e quando tentava programar, travava nas primeiras classes, eu não conseguia nem entender os relacionamentos entre as classes que estava criando.

 

A solução para isso no meu caso (o fórum Imaster me ajudou muito), foi reavaliar o caminho que eu estava seguindo. A conclusão foi: Comece pela PROGRAMAÇÃO e não na UML. Aprende bem orientação a objeto aprendendo e aplicando os princípios da OOP. Só depois se você estiver a vontade com OOP, passe suas classes já concluídas para UML, isso vai te dar uma experiência muito grande em analisar sua aplicação e abrir os olhos para fazer refatoração (melhoramento) do código. Com essa visão aguçada, você está pronto para aprender e entender design pattern. Só de olhar os diagramas de classes (UML) dos pattern você já consegue entender a solução proposta por ele.

 

Concluindo: No meu caso, precisei sofrer para entender que primeiro devo começar pela programação OOP (errando muito e consertando (refatorando) tudo. O erro da uma experiência magnifica! Depois, comecei a passar minhas classes para UML, isso possibilitou que eu visse alguns problemas e melhorasse minha classe. Só depois, comecei a ver design pattern (por último, quando eu já estava familiarizado com OOP).

 

 

Abaixo segue alguns links e livros que foram/estão sendo essências para eu conseguir aprender OOP.

 

 

Site oficial do PHP - Manual OOP

http://php.net/oop

 

Principios da orientação a objeto

DIP - Dependency Inversion Principle

http://www.oodesign.com/dependency-inversion-principle.html

http://brizeno.wordpress.com/category/design-de-software/solid/

http://www.objectmentor.com/resources/articles/dip.pdf

http://www.stubbles.org/archives/85-Dependency-Injection-for-static-methods.html

 

SRP - Single Responsability Principle

http://www.remondo.net/solid-principles-csharp-single-responsibility/

http://www.macoratti.net/08/06/net_srp1.htm

http://www.objectmentor.com/resources/articles/srp.pdf

 

Polimorfismo

http://www-usr.inf.ufsm.br/~rose/curso3/cafe/cap4_Polimorfismo.pdf

 

Encapsulamento

http://www-usr.inf.ufsm.br/~rose/curso3/cafe/cap2_Encapsulamento.pdf

 

LIVROS

Código Limpo - Robert C. Martin

PHP Objects, Patterns and Practice - Matt Zandstra

Use a Cabeça - Analise e Projeto Orientado ao Objetto - Brett McLaughlin, Gary Pollice e David West

 

Cursos

Introdução a Orientação a Objeto - Escola Impacta (Obs.: Só se não tiver noção de como trabalhar com OOP)

Curso online aqui da Imaster de PHP com orientação a Objeto com João Batista. (obs.: O canal de comunicação com o João é muito bom para tirar dúvidas).

PHP com orientação a Objeto e design pattern - Escola 4linux

 

 

Esse foi alguns passos que dei para aprender OOP, fica a dica. Mas lembre-se não é receita de bolo... tudo dependerá do seu esforço.

Compartilhar este post


Link para o post
Compartilhar em outros sites

Henrique Barcelos, obrigado pela colaboração do exemplo de acesso a dados usando o pattern Table gateway. Eu estava fazendo aqui os preparativos para desenvolver uma lib para trabalhar com CRUD e iria ser bem mais complexa do que a exemplificada por você, a sua atendeu muito bem meus objetivos e é muito simples de entender. Parabéns pela ajuda.

 

 

Henrique, quero aproveitar e pedir a sua permissão para adaptar o seu código em minhas aplicações e realizar algumas refatorações.

 

Dai eu colocaria nos arquivos a seguinte documentação:

 

 

/**
* Representação da interface para acesso a dados.
* 
* Nota: "base de dados" é qualquer tipo de armazenamento 
* secundário de arquivos, como bancos de dados, arquivos 
* de texto, arquivos binários, arquivos XML, feeds RSS, etc.
* 
* Parâmetros $data tem a seguinte estrutura:
* $data['nomeDoCampoNaTabela'] = 'valorParaAtribuir';
*
* @author Henrique Barcelos - Imaster.com.br
*/

/**
* Refatoramento
* Autorizado pelo author.
* @author: Felipe Rex
*/

Compartilhar este post


Link para o post
Compartilhar em outros sites

Conforme já mencionei, achei muito útil a classe criada neste tópico pelo Henrique Barcelos (link abaixo). A fim de adequá-la às minhas necessidades e adquirir mais conhecimento na prática, fiz algumas refatorações na lib e gostaria do feedback de todos...

 

O link com a lib original é este:

http://forum.imasters.com.br/topic/451067-myframework/page__view__findpost__p__1784565

 

 

Abaixo segue as refatorações que fiz... estarei comentando as principais alterações.

 

 

Primeiro vou começar a explicar a lib criada PDOFactory, que usa o pattern Factory Method para criar uma interface comum para conectar diferentes driver de banco de dados (UML postado abaixo).

 

Lib PDOFactory

PDOFactory.jpg

 

A criação da lib PDOFactory foi projetada pensando em disponibilizar uma interface comum que possibilite conectar em qualquer base de dados usando um mesmo conjunto de parâmetros.

 

Exemplo de criação de objetos PDO usando diferentes driver, mas com a mesma interface.

 

//configuração

$host = 'localhost';

$nomeBancoDeDados = 'nome_do_banco_de_dados';

$usuario = 'usuario';

$password = 'senha';

 

$mysql = new PDOMysql($host, $nomeBancoDeDados, $usuario, $password);

$mysql->conectar();

 

$postgre = new PDOPostgre($host, $nomeBancoDeDados, $usuario, $password);

$postgre->conectar();

 

$firebird = new PDOFirebird($host, $nomeBancoDeDados, $usuario, $password);

$firebird->conectar();

 

 

Conseguimos conectar a qualquer tipo de driver de banco de dados usando uma mesma interface. Não foi preciso configurar manualmente, por exemplo, a string de host como 'mysql:host=' ou de postgre sendo 'pgsql:host=', foi feito automaticamente com o auxilia do pattern factory method. Além de disponibilizar uma interface simples, a classe disponibiliza manipuladores de conexão o qual torna o uso do banco de dados muito flexível.

 

 

 

Vamos ao que interessa. A lib DataAccessObject.

 

 

UML para visualização

 

 

Lib DataAccessObject (refatorada)

DataAccessObject.jpg

 

 

 

Classe DbAdapter foi refatorado para BancoDeDados.

Descrição: Armazena uma conexão ao banco de dados.

 

Benefícios:

  • Possibilita usarmos diferentes driver de acesso a banco de dados usando uma mesma interface. (lembra da lib PDOFactory?)
  • Possibilita alterarmos em tempo de execução o driver de banco de dados.
  • Com o uso da classe PDOFactory para realizar a conexão com o BD, temos total controle sobre a conexão ao Banco de dados e a desconexão. Desse modo, podemos limitar a conexão com o BD, conectando somente quando houver uma ação a ser realizada, quando a ação é finalizada, a conexão é destruída, evitando que ela fique ociosa e prejudique a conexão de outros usuários ao banco de dados (por exemplo, ocorrendo erro de max_user_connection).

 

 

 

Classe CRUD

Descrição: Operações básicas de manipulação de banco de dados (Creat, Read, Update e Delete).

 

Benefícios:

  • Centraliza as operações ao banco de dados em dois únicos métodos: executar() e consultar().
  • Conexão com o banco de dados só é realizada quando houver uma operação a ser executada, neste caso, somente os métodos executar() e consultar() realizam conexão e desconexão ao banco de dados.
  • Visto que somente dois métodos operam sobre o banco de dados, temos garantia do tipo de retorno. No caso, será apenas dois tipos: quando utilizado o método executar(), o retorno será Inteiro (total de registros afetados) e se for o método consultar() o retorno é array contendo objetos StdClass.

Consequência:

  • Não é possível obter o último id inserido numa tabela com autoincrement usando o método PDO::lastInsertId(). Mas nada impede de implementar na classe sem muita dificuldade.

 

 

 

Interface DataAccessObject

Descrição: interface representando as funcionalidades que será implementado pela classe abstrata DBTableGateway (Pattern Table Data Gateway).

 

 

 

 

Classe abstrata DBTableGateway

Descrição: Chamadas das operações de banco de dados. (Pattern 'Table Data Gateway').

 

Benefícios:

  • Criação do método de validação de consultas Select, garantindo uma seleção válida. DBTableGateway::validarCampoParaSelect().
  • Método getTodosRegistros() refatorado para getRegistro() possibilitando uma maior abstração de consulta ao banco de dados. Agora, além do método ter a funcionalidade de retornar todos os registros da tabela (é só chamar o método sem parâmetro), ele executa qualquer tipo de consulta ao banco de dados.
  • Método getRegistroUnico() possui uma abstração maior possibilitando buscar qualquer registro no BD (pode ser implementado na classe que representa a tabela).
  • Criação do método abstrato getRegistroPorId() (para ser implementado na classe filha- representando a tabela do banco de dados) que pode usar o método getRegistroUnico() para obter um registro usando um ID único da tabela.
     

 

 

Classe MusicaTable

Descrição: Classe filha de DBTableGateway que representa uma Tabela do banco de dados (pattern Table Data Gateway).

 

Benefícios:

  • Única refatoração foi alterar o método getOne() para getRegistroPorId() e fazer encaminhamento da ação para o método mais abstrato getRegistroUnico() da classe-pai.
     

 

 

 

Acredito que as refatorações principais foram mencionadas...

 

 

Lembrando que a ideia aqui é progredirmos em conhecimento. A intenção é gerar uma conversa "saudável" a fim de todos nós tirarmos proveito. O código original é do Henrique Barcelos, ele é o autor e disponibilizou para nós como mencionado no inicio deste post. A intenção foi refatorar para as minhas necessidades e ver a opinião de todos sobre a refatoração, se ficou boa, irá beneficiar a muitos, se não ficou, paciência... com certeza vou tirar muito beneficio dos comentários e das opiniões expressadas.

 

Abaixo segue o código-fonte das refatorações mencionadas. E em seguida tbm os testes para vocês realizarem e, por fim, todos os arquivos zipados para download.

 

 

Lib PDOFactory

/**
* Representação abstrata da conexão ao PDO
*
* @author Felipe
*/

interface IConexaoPDO
{
   public function conectar($host, $dbname, $user, $pass);
}


/**
* Representação de conexão PDO usando o driver Firebird.
*
* @author Felipe
*/


class Firebird implements IConexaoPDO
{
   public function conectar($host = null, $dbname, $user, $pass)
   {
       return new PDO("firebird:dbname={$dbname}", $user, $pass);
   }
}


/**
* Representação de conexão PDO usando o driver Mysql.
*
* @author Felipe
*/


class Mysql implements IConexaoPDO
{
   public function conectar($host, $dbname, $user, $pass)
   {           
       return new PDO("mysql:host={$host};dbname={$dbname}", $user, $pass);
   }
}



/**
* Representação de conexão PDO usando o driver Postgre.
*
* @author Felipe
*/


class Postgre implements IConexaoPDO
{
   public function conectar($host, $dbname, $user, $pass)
   {
       return new PDO("pgsql:host={$host};dbname={$dbname}", $user, $pass);
   }
}


/**
* Representação abstrata da conexão com o PDO.
*
* @author Felipe
*/


abstract class PDOFactory
{

   /**
    * Classe que possibilita realizar a conexão com banco de dados.
    * 
    * @var IConexaoPDO [object]
    */
   protected $driverDB;


   /**
    * Guarda uma conexão PDO com o banco de dados.
    * 
    * @var PDO [object]
    */
   protected $conexaoBD;


   /**
    * Modelo: $configuracao['host']; 
    *         $configuracao['dbname']; 
    *         $configuracao['user']; 
    *         $configuracao['pass']; 
    * @var array
    */
   protected $configuracao;




   public function __construct($host, $dbname, $user, $pass)
   {
       $this->configuracao['host']   = $host;
       $this->configuracao['dbname'] = $dbname;
       $this->configuracao['user']   = $user;
       $this->configuracao['pass']   = $pass;
   }


   public function getConexaoBD()
   {        
       if (!$this->conexaoBD)
       {
           throw new Exception('Nenhuma conexão ativa. É necessário fazer uma conexão ao banco de dados.'  . ' | Método: ' . __METHOD__);
       }

       return $this->conexaoBD;
   }


   public function conectar()
   {
       //checa se precisa criar uma conexão ao bd.
       if ($this->driverDB and !$this->conexaoBD)
       {
           //realiza conexão com o banco de dados usando o driver específico
           $this->conexaoBD = $this->driverDB->conectar(
                                               $this->configuracao['host'], 
                                               $this->configuracao['dbname'], 
                                               $this->configuracao['user'], 
                                               $this->configuracao['pass']
                                               );

           //define as configurações default para o PDO
           $this->configurarPDO();
       }
   }

   public function desconectar()
   {
       //desconecta do banco de dados
       //Nota.: Ao destruir uma classe PDO, automaticamente desconecta do DB
       $this->conexaoBD = null;
   }


   private function configurarPDO()
   {
       #CONFIGURAÇÃO PRÉ-DEFINIDAS PARA USO DO PDO


       //lançamento de erro: Exception
       $this->conexaoBD->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
       //iteração de resource: modo Objeto
       $this->conexaoBD->setAttribute(PDO::ATTR_DEFAULT_FETCH_MODE, PDO::FETCH_OBJ);
   }


   /**
    * Garante que a conexão com o PDO seja interrompida quando o objeto for destruido.
    * 
    * @return void
    */
   public function __destruct()
   {        
       $this->desconectar();
   }
}

/**
* Representação do factory (criação) de conexão PDO com driver Firebird.
*
* @author Felipe
*/

class PDOFirebird extends PDOFactory
{

   public function conectar()
   {
       if (!$this->driverDB)
       {
           //defini o driver que representa o banco de dados a ser utilizado
           $this->driverDB = new Firebird();
       }

       if (!$this->conexaoBD)
       {
           //chama método pai que realiza a conexão com o banco de dados
           parent::conectar();
       }
   }
}


/**
* Representação do factory (criação) de conexão PDO com driver Mysql.
*
* @author Felipe
*/

class PDOMysql extends PDOFactory
{
   public function conectar()
   {
       if (!$this->driverDB)
       {
           //defini o driver que representa o banco de dados a ser utilizado
           $this->driverDB = new Mysql();
       }

       if (!$this->conexaoBD)
       {
           //chama método pai que realiza a conexão com o banco de dados
           parent::conectar();
       }
   }

}


/**
* Representação do factory (criação) de conexão PDO com driver Postgre.
*
* @author Felipe
*/

class PDOPostgre extends PDOFactory
{
   public function conectar()
   {
       if (!$this->driverDB)
       {
           //defini o driver que representa o banco de dados a ser utilizado
           $this->driverDB = new Postgre();
       }

       if (!$this->conexaoBD)
       {
           //chama método pai que realiza a conexão com o banco de dados
           parent::conectar();
       }
   }
}


 

 

 

Lib DataAccessObject

 

 


/**
* Representação da interface para acesso a dados.
* 
* Nota: "base de dados" é qualquer tipo de armazenamento 
* secundário de arquivos, como bancos de dados, arquivos 
* de texto, arquivos binários, arquivos XML, feeds RSS, etc
*
* @author Henrique Barcelos - Imaster.com.br
*/

/**
* Refatoramento
* com permissão de Henrique Barcelos
* @author: Felipe Massariol
*/

interface DataAccessObject 
{

   /**
    * Insere um array de dados na base de dados.
    *
    * @param  array $data : estrutura $data['nomeDoCampoNaTabela'] = 'valorParaAtribuir'
    * @return int
    */
   public function inserir(array $data);


   /**
    * Atualiza os dados na base de dados sob a condição 
    * especificada para os dados informados.
    *
    * @param  array $data : estrutura $data['nomeDoCampoNaTabela'] = 'valorParaAtribuir'
    * @param  string $condicao : Condição para busca dos dados a serem atualizados
    * @return int
    */
   public function atualizar(array $data, $condicao);


   /**
    * Remove os dados da base de dados sob a condição especidicada.
    *
    * @param  string $condicao
    * @return int
    */
   public function deletar($condicao);


   /**
    * Retorna registros da base de dados. Caso não seja passado nenhum parâmetro, retorna todos os registros da tabela.
    *
    * @param string $condicao : condição a ser usada para seleionar registros. Campo opcional.
    * @param array $listaCamposRetorno : Array contendo valores com os campos a serem retornados. Campo opcional.
    * @return array
    */
   public function getRegistros($condicao = null, $listaCamposRetorno = array());


   /**
    * Retorna um registro específico da base de dados.
    * Tal registro precisa ter um identificador único.
    *
    * @param string $condicao : condição a ser usada para seleionar registros
    * @param array $listaCamposRetorno : Array contendo valores com os campos a serem retornados. Campo opcional.
    * @return StdClass [object]
    */
   public function getRegistroUnico($condicao, $listaCamposRetorno = array());


   /**
    * Obtêm registro único com base no campo PRIMARY (índice)
    * 
    * @param  mixed $id : valor a ser setado para buscar o registro.
    * @return StdClass [object]
    */
   public function getRegistroPorId($id);
}



/**
* Representação abstrata para trabalhar com base de dados.
* 
* Nota: Implementação básica do padrão de projeto TableDataGateway.
*
* @author Henrique Barcelos - Imaster.com.br
*/

/**
* Refatoramento
* com permissão do autor Henrique Barcelos
* @author: Felipe Massariol
*/

abstract class DBTableGateway implements DataAccessObject
{

   /**
    * @var BancoDeDados [object]
    */
   protected $bancoDeDados;


   /**
    * nome da tabela a qual o gateway se refere.
    * @var string
    */
   protected $nomeTabela;


   /**
    * Lista de nomes dos campos existentes na tabela
    * @var array
    */
   protected $camposTabela = array();


   /**
    * Agrega o manipulador da base de dados.
    *
    * @param BancoDeDados $bd
    * @return void
    */
   public function __construct(BancoDeDados $bd){
       $this->bancoDeDados = $bd;
   }


   /**
    * (non-PHPdoc)
    * @see DataAccessObject::inserir()
    */
   public function inserir(array $data)
   {
       //checa se cada campo existe na tabela, caso não, remove da array
       $inserirDado = $this->validarCampo($data);

       if(empty($inserirDado)) {
           throw new Exception('Nenhum campo para inserir na tabela ' . $this->nomeTabela . ' | Método: ' . __METHOD__);
       }

       //Chama no adapter a função de inserção, a nível de banco de dados 
       return $this->bancoDeDados->CRUD->inserir($this->nomeTabela, $inserirDado);
   }


   /**
    * (non-PHPdoc)
    * @see DataAccessObject::atualizar()
    */
   public function atualizar(array $data, $condicao) {
       //checa se cada campo existe na tabela, caso não, remove da array
       $camposParaAtualizar = $this->validarCampo($data);

       if(empty($camposParaAtualizar)) {
           throw new Exception('Nenhum campo para atualizar na tabela ' . $this->nomeTabela . ' | Método: ' . __METHOD__);
       }


       //realiza atualização
       return $this->bancoDeDados->CRUD->atualizar($this->nomeTabela, $camposParaAtualizar, $condicao);
   }


   /**
    * (non-PHPdoc)
    * @see DataAccessObject::deletar()
    */
   public function deletar($condicao)
   {
       return $this->bancoDeDados->CRUD->deletar($this->nomeTabela, $condicao);
   }


   /**
   * (non-PHPdoc)
   * @see DataAccessObject::getTodosRegistros()
   */
   public function getRegistros($condicao = null, $listaCamposRetorno = array())
   {
       //valida os campos a serem retornados pelo SELECT
       $camposRetorno = $this->validarCampoParaSelect($listaCamposRetorno);

       if ($condicao)
       {
           $condicao = " WHERE {$condicao}";
       }

       $sql = "SELECT {$camposRetorno} FROM " . $this->nomeTabela . $condicao;

       return $this->bancoDeDados->CRUD->getRegistros($sql);
   }


   /**
   * Retorna registro único
   * @see DataAccessObject::getRegistroUnico()
   */
   public function getRegistroUnico($condicao, $listaCamposRetorno = array())
   {
       //valida os campos a serem retornados pelo SELECT
       $camposRetorno = $this->validarCampoParaSelect($listaCamposRetorno);

       if(empty($condicao)) 
       {
           throw new Exception('Nenhuma condição foi informada para fazer a seleção na tabela ' . $this->nomeTabela . ' | Método: ' . __METHOD__);
       }


       $sql = "SELECT {$camposRetorno} FROM " . $this->nomeTabela . " WHERE {$condicao} LIMIT 1";

       return $this->bancoDeDados->CRUD->getRegistroUnico($sql);
   }


   /**
    * Verifica se índices de um array (representando os campos da tabela) existem realmente na tabela.
    * Nota: Se o campo não existir na tabela, remove o índice do array junto com o seu valor.
    * 
    * @param  array $data : dados para entrada, sendo a key o nome do campo da tabela e o seu respectivo valor.
    * @return array       : os dados filtrados
    */
   private function validarCampo(array $data) {

       $dadosVerificados = array();

       foreach($data as $key => $valor) {
           //checa se campo existe na tabela
           if(in_array($key, $this->camposTabela)) {
                   //adiciona à lista de campos válidos
                   $dadosVerificados[$key] = $valor;
           }
       }

       //Retornamos os dados válidos
       return $dadosVerificados;
   }


   /**
    * Validação de campos para serem usado em comandos sql SELECT
    * 
    * @param  array $listaCamposRetorno : Valores com os nomes dos campos para usar em comandos SQL - Select
    * @return string
    */
   private function validarCampoParaSelect($listaCamposRetorno)
   {
       //checa se valor é um array e se tem algum valor definido
       if (!is_array($listaCamposRetorno) or !count($listaCamposRetorno))
       {
           //limpa valor
           $listaCamposRetorno = null;


           //retorna valor default
           return $camposRetorno = '*';
       }


       //cria array com índice e valor iguais para poder fazer filtragem com verificarCampo()
       $listaCamposRetorno = array_combine($listaCamposRetorno, $listaCamposRetorno);

       //checa se cada campo existe na tabela, caso não, remove da array
       $camposRetorno = $this->validarCampo($listaCamposRetorno);

       //transforma valores da array em string separados por virgula
       $camposRetorno = implode(', ', $camposRetorno);

       return $camposRetorno;
   }

   /**
    * (non-PHPdoc)
    * @see DataAccessObject::getRegistroPorId()
    */
   public function getRegistroPorId($id) {}

}



/**
* Representação para trabalhar com a tabela 'Noticias'
*
* @author Henrique Barcelos - Imaster.com.br
*/

/**
* Refatoramento
* com permissão de Henrique Barcelos
* @author: Felipe Massariol
*/
class MusicasTable extends DBTableGateway
{

   /**
    * @var string 
    */
   protected $nomeTabela = 'musicas';


   /**
    * @see DBTableGateway::camposTabela
    * @var array
    */
   protected $camposTabela = array(
       0  => 'ID', 
       1  => 'nome', 
       2  => 'artista', 
       3  => 'data', 
       );


   /**
   * Retorna registro único
   * @see DataAccessObject::getRegistroPorId()
   */
   public function getRegistroPorId($id)
   {           
           return $this->getRegistroUnico("ID = {$id}");
   }
}



/**
* Implementação do padrão Adapter para conexão com
* o banco de dados. Abstrar a maior parte das funções
* sobre um banco de dados sob uma interface mais simples
* 
* @author henrique
*/
class BancoDeDados 
{

   /**
    * Armazena o driver de conexão com o banco de dados
    * @var PDOFactory [object]
    */
   protected $PDO;


   /**
    * Armazena ações comuns ao banco de dados.
    * @var CRUD [object]
    */
   public $CRUD;


   /**
    * Define a conexão com a base de dados
    * 
    * @param PDOFactory $pdo
    * @return void
    */
   public function __construct(PDOFactory $pdo)
   {
       $this->setConexao($pdo);

       //Agrega à clase as ações comuns ao banco de dados
       $this->CRUD = new CRUD($this->PDO);
   }


   /**
    * Define a conexão com a base de dados
    * 
    * @param PDOFactory $pdo
    * @return void
    */
   public function setConexao(PDOFactory $pdo)
   {
       //destroi objeto PDOFactory forçando destruição da conexão ativa do PDO (se houver)
       $this->PDO = null;

       //guarda nova referência do PDOFactory
       $this->PDO = $pdo;
   }

}




/**
* Representação das ações comuns num banco de dados. 
* CRUD - Creat, Read, Update e Delete
*
* @author Felipe
*/

class CRUD
{
   /**
    * Armazena o driver de conexão com o banco de dados
    * @var PDOFactory [object]
    */
   public $PDO;


   /**
    * Define a conexão com a base de dados.
    * 
    * @param PDOFactory $pdo : o PDOFactory é passado por referência, se houver mudança de driver, o crud reconhecerá automaticamente.
    * @return void
    */
   public function __construct(PDOFactory &$pdo)
   {
       //define a propriedade com o valor por referência, garantindo que sempre que houver alterações na variavel, a propriedade automaticamente apontará para o novo valor.
       $this->PDO = &$pdo;
   }  


   /**
    * Adiciona novo registro à base de dados.
    * 
    * @param string $nomeTabela : nome da tabela a ser trabalhada
    * @param array  $dados      : estrutura $dados['nomeDoCampoNaTabela'] = 'valorParaAtribuir'
    *
    * @return int      
    */
   public function inserir($nomeTabela, array $dados)
   {
       //cria array
       $listaSqlValor = array();

       //cria array com os nomes dos campos (pegando os nomes dos índices de $dados)
       $listaCampoTabela = array_keys($dados);

       //converte array com os nome dos campos da tabela em string
       $nomeCampo = implode(', ', $listaCampoTabela);

       //trata os valores dos campos adicionando segurança
       foreach($dados as $valorCampo){
           //realiza garantia que o valor do campo não vai quebrar o comando sql
           $listaSqlValor[] = $this->quote($valorCampo);
       }

       //converte array com os valores dos campos em string
       $valorCampo  = implode(', ', $listaSqlValor);


       $stmt = "INSERT INTO {$nomeTabela} ({$nomeCampo}) VALUES ({$valorCampo})";

       //executa comando SQL retornando o número de registros afetados
       return $this->executar($stmt);
   }


   /**
    * Atualiza registro duma base de dados.
    * 
    * @param string $nomeTabela : nome da tabela a ser trabalhada
    * @param array  $dados      : estrutura $dados['nomeDoCampoNaTabela'] = 'valorParaAtribuir' 
    * @param string $condicao   : condição usada para realizar a atualização
    *
    * return int        
    */

   public function atualizar($nomeTabela, array $dados, $condicao)
   {
       //cria array
       $listaSqlAlteracao = array();

       //armazena num array cada campo que será alterado com seu respectivo valor
       foreach($dados as $nomeCampo => $valorCampo){
           //realiza garantia que o valor do campo não vai quebrar o comando sql
           $listaSqlAlteracao[] = $nomeCampo . ' = ' . $this->quote($valorCampo);
       }

       //converte array com as linhas de alteração em string
       $sqlAlteracao  = implode(', ', $listaSqlAlteracao);


       $stmt = "UPDATE {$nomeTabela} SET {$sqlAlteracao} WHERE {$condicao}";


       //executa comando SQL retornando o número de registros afetados
       return $this->executar($stmt);
   }


   /**
    * Remove registro da base de dados.
    * 
    * @param  string $nomeTabela : nome da tabela a ser trabalhado
    * @param  string $condicao   : condição usada para realizar a remoção 
    * @return int
    */
   public function deletar($nomeTabela, $condicao)
   {   
       if ($condicao)
       {
           $condicaoExiste = "WHERE {$condicao}";
       }

       $stmt = "DELETE FROM {$nomeTabela} {$condicaoExiste}";


       //executa comando SQL retornando o número de registros afetados
       return $this->executar($stmt);
   }


   /**
    * Obtêm todos os resultados de uma query genérica.
    * 
    * @param  string   $sql
    * @return array
    */
   public function getRegistros($sql)
   {
       $listaRegistro = array();

       //executa o comando SQL obtendo um objeto do tipo PDOStatement
       $resultadoSQL = $this->consultar($sql);

       //retorna array contendo os registros
       return $resultadoSQL->fetchAll();
   }


   /**
    * Obtêm o primeiro registro resultante da consulta sql
    * 
    * @param  string	$sql
    * @return StdClass [object]
    */
   public function getRegistroUnico($sql)
   {
       //executa o comando SQL obtendo um objeto do tipo PDOStatement
       $resultadoSQL = $this->consultar($sql);

       //obtêm o primeiro registro do PDOStatement
       $resultadoSQL = $resultadoSQL->fetch();

       return $resultadoSQL;
   }    


   /**
    * Executa uma query genérica na base de dados retornando um inteiro informando quantos registros foram afetados.
    * 
    * @param  string $sql
    * @return int  : quantidade de registros afetados
    */
   public function executar($sql)
   {
      $this->PDO->conectar();

      $conexaoAtiva = $this->PDO->getConexaoBD();

      //executa comando sql com exec() da PDO retornando o número de registro afetados
      $resultadoSQL = $conexaoAtiva->exec($sql);

      //desconecta do banco de dados
      $this->PDO->desconectar();

      return $resultadoSQL;
   }


   /**
    * Executa uma query genérica na base de dados retornando array com os registros resultantes do comando SQL.
    * 
    * @param  string $sql
    * @return array  : lista de registros retornados.
    */
   public function consultar($sql)
   {
      $this->PDO->conectar();

      $conexaoAtiva = $this->PDO->getConexaoBD();

      //executa comando sql usando método query() da PDO para retornar array de resultados
       $resultadoSQL = $conexaoAtiva->query($sql);

      //desconecta do banco de dados
      $this->PDO->desconectar();


      return $resultadoSQL;
   }


   /**
    * Coloca escape de caractere ao texto. Garante segurança aos dados inseridos na tabela.
    * 
    * @param  string $texto
    * @return string
    */
   private function quote($dado)
   {
       //checa se valor não é número
       if(is_string($dado)) {

           //conecta usando PDO para poder usar o método PDO::quote()
           $this->PDO->conectar();

           //adiciona escape de caracteres como '`"\ usando quote do PDO
           $textoFormatado = $this->PDO->getConexaoBD()->quote(stripslashes($dado));

           //desconecta do banco de dados
           $this->PDO->desconectar();

           return $textoFormatado;
       }

       return $dado;
   }
}

 

 

TESTES

 

 

Baixe o arquivo zip que lá contém os testes, só lembre de criar a tabela na base de dados, você pode rodar a página CriarTabela.php que cria automaticamente, só lembre de alterar a variável $nomeBancoDeDados.

 

Comando sql para criar a tabela para rodar os testes:

CREATE TABLE `musicas` (

`ID` int(11) NOT NULL AUTO_INCREMENT,

`musicaMusica` varchar(100) NOT NULL,

`artista` varchar(100) NOT NULL,

`data` DATE NOT NULL,

PRIMARY KEY (`ID`)

);

 

Descompacte os arquivos numa mesma pasta. Qualquer dúvida, post aqui...

 

 

Download do source: http://felipemassariol.com.br/DataAcessObject.zip

 

 

Deem seus comentários e sugestões.

Compartilhar este post


Link para o post
Compartilhar em outros sites

Considerações

 

Sobre o padrão Factory Method:

Muito bem empregado, de acordo com os conceitos originais da ideia...

Entretanto, dada a flexibilidade o PHP eu acho o método um tanto custoso...

 

Toda vez que eu quiser adicionar suporte a um novo SGBD eu tenho que criar mais 2 classes, uma Factory e uma implementação.

Isso vai fazendo crescer horizontalmente a árvore de herança e, normalmente, esse tipo de coisa é problemático.

Imagine que por alguma razão você precise adicionar métodos na interface. Isso fará com que você tenha que criar uma implementação em todas as classes que implementam a interface.

No seu caso, até que o problema não é tão grande, afinal, o número de SGBDs nem é tão grande assim (mas também não é pequeno), mas em outras situações, pode se tornar problemático.

 

Como eu faço atualmente:

class Db {
private function __construct(){ }

public static function factory($adapter, array $config){
	$className = 'Db_Adapter_'.ucfirst($adapter);
	if(class_exists($className)){
		return new $className($config);
	} else {
		throw new Exception(sprintf('Adapter "%s" inexistente!', $adapter));
	}
}
}

 

Com isso aí, já mato a necessidade do Factory Method tradicional, porque a criação de conexão bancos de dados não varia muito, você simplesmente passa os dados e a conexão é feita.

 

Outra coisa:

class Firebird implements IConexaoPDO
{
   public function conectar($host = null, $dbname, $user, $pass)
   {
       return new PDO("firebird:dbname={$dbname}", $user, $pass);
   }
}


/**
* Representação de conexão PDO usando o driver Mysql.
*
* @author Felipe
*/


class Mysql implements IConexaoPDO
{
   public function conectar($host, $dbname, $user, $pass)
   {           
       return new PDO("mysql:host={$host};dbname={$dbname}", $user, $pass);
   }
}



/**
* Representação de conexão PDO usando o driver Postgre.
*
* @author Felipe
*/


class Postgre implements IConexaoPDO
{
   public function conectar($host, $dbname, $user, $pass)
   {
       return new PDO("pgsql:host={$host};dbname={$dbname}", $user, $pass);
   }
}

 

Você percebe que está duplicando código? Tudo bem que não é muita coisa, mas vai que um dia muda o jeito de se conectar ao PDO, vai que mudam o nome da classe (improvável? Mas é possível)? Você vai ter que alterar 3 coisas ao invés de uma...

 

E se fosse assim?

 

abstract class ConexaoPDOAbstract implements IConexaoPDO {
abstract protected function getDsn($host, $dbname, $user, $pass); // : String

public function conectar($host, $dbname, $user, $pass) {
   	return new PDO($this->getDsn(), $user, $pass);
}
}

class Postgre implements IConexaoPDO
{
   public function getDsn($host, $dbname, $user, $pass)
   {
       return "pgsql:host={$host};dbname={$dbname}";
   }
}

class Postgre implements IConexaoPDO
{
   public function getDsn($host, $dbname, $user, $pass)
   {
       return "mysql:host={$host};dbname={$dbname}";
   }
}


class Firebird implements IConexaoPDO
{
   public function getDsn($host, $dbname, $user, $pass)
   {
       return "firebird:dbname={$dbname}";
   }
}

_____________________________

 

Você também se esqueceu completamente de possibilitar a configuração da porta de acesso ao banco de dados... Nem sempre eles estão escutando a porta padrão (3306 do MySQL, 5432 do Postgre), pode ser que haja outra aplicação nessas portas...

 

Fora isso, eu realmente não entendi porque existe a classe BancoDeDados... Por que você não utiliza somente a classe CRUD?

 

public function __construct(PDOFactory &$pdo)
   {
       //define a propriedade com o valor por referência, garantindo que sempre que houver alterações na variavel, a propriedade automaticamente apontará para o novo valor.
       $this->PDO = &$pdo;
   }  

Está querendo manter compatiblidade com o PHP4???

Se não, essa referência aí é totalmente desnecessária, pois o PHP5 por padrão passa parâmetros que são objetos por valor-referência (que é quase a mesma coisa que referência).

_____________________________

 

Também não entendi porque você não consegue mais retornar o lastInsertId...

_____________________________

 

Por último, não tem muito a ver em si com o a ideia ou a implementação, mas você deve seguir um padrão de codificação: escolhe, inglês OU português (e é um ou exclusivo).

Muita gente não se importa, eu não acho uma coisa legal escrever 'getResutado', por exemplo. Pegar/retornarResultado ou getResult, por/colocarResultado ou setResult.

Factory ou Fábrica??? TableGateway ou PortalTabela??? Adapter ou Adaptador???

 

A escolha é sua, mas mantenha um padrão. Entretanto, o mundo da programação é dominado pelo inglês, ajuda bastante a aprender o idioma programar utilizando-o.

 

Compartilhar este post


Link para o post
Compartilhar em outros sites

Henrique, primeiramente muito obrigado pela analise e opinião expressada.

 

 

Sobre o padrão Factory Method:

Muito bem empregado, de acordo com os conceitos originais da ideia...

Entretanto, dada a flexibilidade o PHP eu acho o método um tanto custoso...

 

Toda vez que eu quiser adicionar suporte a um novo SGBD eu tenho que criar mais 2 classes, uma Factory e uma implementação.

Isso vai fazendo crescer horizontalmente a árvore de herança e, normalmente, esse tipo de coisa é problemático.

Imagine que por alguma razão você precise adicionar métodos na interface. Isso fará com que você tenha que criar uma implementação em todas as classes que implementam a interface.

No seu caso, até que o problema não é tão grande, afinal, o número de SGBDs nem é tão grande assim (mas também não é pequeno), mas em outras situações, pode se tornar problemático.

 

Como eu faço atualmente:

class Db {

private function __construct(){ }

 

public static function factory($adapter, array $config){

$className = 'Db_Adapter_'.ucfirst($adapter);

if(class_exists($className)){

return new $className($config);

} else {

throw new Exception(sprintf('Adapter "%s" inexistente!', $adapter));

}

}

}

 

 

Com isso aí, já mato a necessidade do Factory Method tradicional, porque a criação de conexão bancos de dados não varia muito, você simplesmente passa os dados e a conexão é feita.

 

 

Concordo que é preciso criar dois novos arquivos para adicionar suporte a um novo SGBD, mas, acredito que é um trabalho relativamente pequeno e simples comparado ao suporte que a lib fornece sobre a conexão estabelecida num determinado driver de banco de dados.

 

A classe PDOFactory está implementada para ser flexível ao conectar num banco de dados. Por exemplo, eu posso definir qual driver de banco de dados vou trabalhar (mysql/postgre/firebird...etc) e só requisitar a conexão quando precisar dela. No caso da classe CRUD que faz uso do PDOFactory, podemos ver a flexibilidade dessa implementação onde somente os métodos executar() e consultar() usam uma conexão ao banco de dados, então, estes métodos possuem uma conexão e desconexão ao banco de dados. Isso garante que a conexão será apenas e unicamente para realizar a operação no banco, logo depois que a operação for concluída, a conexão é destruída liberando para outros leitores do site (digno de nota que conexões ao mesmo tempo num banco de dados tem limite, essa flexibilidade da PDOFactory ajuda a não tornar ociosa a conexão).

 

Eu já tive problema com max_user_connection e não foi simples solucionar (setar para o mysql aceitar mais conexões simultâneas não é a melhor solução) -> Clique aqui

 

 

Exemplo prático para entender a flexibilidade dessa implementação:

 

 

   $host             = 'localhost';
   $nomeBancoDeDados = 'radio';
   $usuario          = 'root';
   $password         = 'vertrigo';
   $porta            = '';

   $mysql = new PDOMysql($host, $nomeBancoDeDados, $usuario, $password, $porta);
   [b]$mysql->conectar();[/b]

   //executa método query do PDO
   $steatement = $mysql->getConexaoBD()->query("SELECT nome FROM musicas LIMIT 0,10");

   while ($row = $steatement->fetch(PDO::FETCH_OBJ))
   {
       echo $row->nome . '</br>';
   }

   [b]$mysql->desconectar();[/b]

   //Uso uma conexão ao banco de dados somente quando preciso dela, liberando logo em seguida.

 

 

Sem o uso do PDO Factory

 

 


  $host             = 'localhost';
   $nomeBancoDeDados = 'radio';
   $usuario          = 'root';
   $password         = 'vertrigo';
   $porta            = '';

   $mysql = new PDO("mysql:host={$host};dbname={$nomeBancoDeDados};port={$porta}", $usuario, $password);

   //executa método query do PDO
   $steatement = $mysql->query("SELECT nome FROM musicas LIMIT 0,10");

   while ($row = $steatement->fetch(PDO::FETCH_OBJ))
   {
       echo $row->nome . '</br>';
   }

   //A conexão continua aberta... ociosa

 

Se alguém souber se o PDO destrói conexões ociosas, comente... Eu fiz um script para realizar este teste e pelo que notei, a conexão fica ativa durante 12 segundos após a execução do comando e depois é liberada... Fiz um teste bem simples... Mesmo que seja 12 segundos, é um tempo considerável se estivermos falando de centenas de usuários conectando ao mesmo tempo no bd. Com o PDOFactory, garantimos a liberação da conexão ao banco de dados a outros usuários de forma imediata.

 

 

Acredito que esta implementação é válida e vale o trabalho de ter que criar duas novas classes para adicionar suporte a um novo SGBD (visto que não existe muitos SGBD, geralmente, será necessário implementar só os quais vai usar). Esse é o meu ponto de vista, mas peço que analisem e expressem suas opiniões, conhecimento nunca é demais.

 

 

Você percebe que está duplicando código? Tudo bem que não é muita coisa, mas vai que um dia muda o jeito de se conectar ao PDO, vai que mudam o nome da classe (improvável? Mas é possível)? Você vai ter que alterar 3 coisas ao invés de uma...

 

E se fosse assim?

 

Muito boa a dica, sem dúvida a refatoração indicada é muito boa. Já implementei ela aqui...

 

Você também se esqueceu completamente de possibilitar a configuração da porta de acesso ao banco de dados... Nem sempre eles estão escutando a porta padrão (3306 do MySQL, 5432 do Postgre), pode ser que haja outra aplicação nessas portas...

 

Na verdade eu até tinha pensado em implementar a configuração da porta, mas, naquele momento pensei só em mim (consequência de muitas horas de programação :(/>/>/>/>/>/> ) e evitei trabalho extra visto que uso somente as portas default... Mas visto a necessidade, já implementei no código. Ficou melhor com a opção de setar a porta. Valeu a indicação da necessidade dessa implementação :)/>/>/>/>/>/>/>

 

 

Fora isso, eu realmente não entendi porque existe a classe BancoDeDados... Por que você não utiliza somente a classe CRUD?

 

A principal razão foi seguir o princípio SRP, na classe BancoDeDados eu mantive a responsabilidade de manipular a conexão com o banco de dados, onde possibilita alterar em tempo de execução (BancoDeDados::setConexao()) o driver a ser usado para realizar conexões ao banco de dados.

 

Assim, posso trabalhar com um banco mysql executar algumas ações e... mudar o driver para postgre para manipular um outro banco de dados. Achei melhor separar essa responsabilidade na classe BancoDeDados ao invés de deixá-la na CRUD que tem a responsabilidade de trabalhar com os registros. Fique a vontade para fazer qualquer observação... se tiver uma sugestão, posso virar a classe de cabeça pra baixo e implementar para melhorá-la.... a ideia é ter uma lib consistente.

 

public function __construct(PDOFactory &$pdo)

{

//define a propriedade com o valor por referência, garantindo que sempre que houver alterações na variavel, a propriedade automaticamente apontará para o novo valor.

$this->PDO = &$pdo;

}

 

Está querendo manter compatiblidade com o PHP4???

Se não, essa referência aí é totalmente desnecessária, pois o PHP5 por padrão passa parâmetros que são objetos por valor-referência (que é quase a mesma coisa que referência).

 

Não entendi... estou usando a versão 5.3.14 e aqui não pega parametro do tipo objeto como valor-referência. Fiz vários testes e não deu. Se eu não setar o parâmetro e a passagem do parâmetro para a propriedade por referência, não aponta para o mesmo lugar na memória.

 

Exemplo:

 

    $host             = 'localhost';
   $nomeBancoDeDados = 'radio';
   $usuario          = 'root';
   $password         = 'vertrigo';

   $mysql   = new PDOMysql($host, $nomeBancoDeDados, $usuario, $password);
   $postgre = new PDOPostgre($host, $nomeBancoDeDados, $usuario, $password);

   //banco de dados a ser usado
   $BD = new BancoDeDados($mysql);
   $BD->setConexao($postgre);

 

 

Com o código acima e a classe CRUD com o método implementado dessa forma:

public function __construct(PDOFactory &$pdo)
   {
       //define a propriedade com o valor por referência, garantindo que sempre que houver alterações na variavel, a propriedade automaticamente apontará para o novo valor.
       $this->PDO = &$pdo;
   } 

 

 

A propriedade $this->PDO se der um get_class() vai apontar para PDOPostgre, agora se tirar qualquer símbolo & tanto do parâmetro como da definição do valor da propriedade PDO, vai apontar para PDOMysql, ou seja, a passagem por referência não foi feita por default.

 

Também não entendi porque você não consegue mais retornar o lastInsertId...

 

É porque a conexão é destruida sempre que é executado um comando no banco de dados. Se a conexão for destruida, a referência do LastInsertId() é perdida. Mas, nada impede de implementar uma alternativa... é só criar uma propriedade e armazenar nela o ID sempre que um insert for executado... No meu caso, nao vejo necessidade, mas fica fácil ter o valor...

 

Por último, não tem muito a ver em si com o a ideia ou a implementação, mas você deve seguir um padrão de codificação: escolhe, inglês OU português (e é um ou exclusivo).

Muita gente não se importa, eu não acho uma coisa legal escrever 'getResutado', por exemplo. Pegar/retornarResultado ou getResult, por/colocarResultado ou setResult.

Factory ou Fábrica??? TableGateway ou PortalTabela??? Adapter ou Adaptador???

 

A escolha é sua, mas mantenha um padrão. Entretanto, o mundo da programação é dominado pelo inglês, ajuda bastante a aprender o idioma programar utilizando-o.

 

 

Muito boa a dica. Eu realmente uso principalmente as palavras get e set nos métodos (acho que uso outros tbm, mas com menos frequencia), virou costume mesmo... Era necessário que alguém pegasse no meu pé ehehhe, obrigado mais uma vez Henrique ;)/>

 

No seu ponto de vista, é bom fazer tudo numa única lingua, ou você acha que não há problema por exemplo, escrever toda a parte de programação em inglês (nomes de métodos, de classes, propriedades...) e a documentação em português? Fica uma bagunça do mesmo jeito? É que não tenho um conhecimento tão amplo ainda para escrever textos em inglês (por exemplo o que um método faz), porém, para palavras e pequenas frases não tenho problema.

Compartilhar este post


Link para o post
Compartilhar em outros sites
No seu ponto de vista, é bom fazer tudo numa única lingua, ou você acha que não há problema por exemplo, escrever toda a parte de programação em inglês (nomes de métodos, de classes, propriedades...) e a documentação em português? Fica uma bagunça do mesmo jeito? É que não tenho um conhecimento tão amplo ainda para escrever textos em inglês (por exemplo o que um método faz), porém, para palavras e pequenas frases não tenho problema.

 

Documentação não tem problema nenhum, você faz no idioma que preferir... A ideia é só NÃO MISTURAR... ahsahsuhsahususa

 

É porque a conexão é destruida sempre que é executado um comando no banco de dados. Se a conexão for destruida, a referência do LastInsertId() é perdida. Mas, nada impede de implementar uma alternativa... é só criar uma propriedade e armazenar nela o ID sempre que um insert for executado... No meu caso, nao vejo necessidade, mas fica fácil ter o valor...

 

Péssima prática... Você gera um overhead se ficar abrindo e fechando conexões. Dá trabalho conseguir uma conexão, há um ganho considerável no tempo de resposta. O ideal é abrir a conexão uma vez só durante a requisição.

 

Não entendi... estou usando a versão 5.3.14 e aqui não pega parametro do tipo objeto como valor-referência. Fiz vários testes e não deu. Se eu não setar o parâmetro e a passagem do parâmetro para a propriedade por referência, não aponta para o mesmo lugar na memória.

:seta: http://www.php.net/manual/en/language.references.php

 

A principal razão foi seguir o princípio SRP, na classe BancoDeDados eu mantive a responsabilidade de manipular a conexão com o banco de dados, onde possibilita alterar em tempo de execução (BancoDeDados::setConexao()) o driver a ser usado para realizar conexões ao banco de dados.

Acho que nesse caso você tá usando HRP (Half Resposibility Principle) :grin:/>... haushasahsaus... Sim, acabei de inventar.

 

Você está distribuindo em duas classes o que poderia colocar em uma só... Basta você mover os métodos de BancoDeDados para CRUD ou vice-versa. Você ainda vai poder alterar a conexão se mantiver o método setConexao.

Compartilhar este post


Link para o post
Compartilhar em outros sites

Péssima prática... Você gera um overhead se ficar abrindo e fechando conexões. Dá trabalho conseguir uma conexão, há um ganho considerável no tempo de resposta. O ideal é abrir a conexão uma vez só durante a requisição.

 

Pode até ser que haja perda de performance por abrir e fechar a conexão ao invés de mantê-la aberta... Mas por outro lado se manter aberta e ociosa pode perder usuários para o site. Ocorreu comigo no passado, por ter muitos usuários conectados ao mesmo tempo, esgotou o limite de conexões ao bd, e os usuários que entravam no site posteriormente, ficava sem ver conteúdo algum porque nao havia conexão com o bd. Então acredito que é preciso pesar as consequência... Eu acredito que há uma sobrecarga se uma página fizer 10 requisições e cada uma abrir e fechar a conexão com o bd, mas tbm há uma perda muito grande se mantiver a conexão aberta durante todas as 10 requisições e finalizar somente quando conclui-las. Pensando em números grandes de acesso simultâneos (meu caso), talvez seja melhor sobrecarregar o servidor que é dedicado e aguenta do que perder dinheiro por não exibir conteúdo ao usuário por falta de conexão ao bd.

 

Não entendi... estou usando a versão 5.3.14 e aqui não pega parametro do tipo objeto como valor-referência. Fiz vários testes e não deu. Se eu não setar o parâmetro e a passagem do parâmetro para a propriedade por referência, não aponta para o mesmo lugar na memória.

 

:seta:/>/> http://www.php.net/manual/en/language.references.php

 

Li o link (agradeço pela indicação da fonte para pesquisa) e notei esse ponto:

Desde o PHP 5, new retorna referência automaticamente, então usar =& neste contexto é obsoleto e produz mensagem de nível E_STRICT.

Acho que é essa nota que você mencionou sobre a mudança no php 5, certo? Pelo o que eu vi, essa mudança de tornar obsoleto o uso de referência é quando se usa com o new, no caso aplicado, trata-se de parâmetro de função, neste caso é necessário a passagem por referência e para transmitir a referência à propriedade, tbm é necessário o uso do &. Como te falei anteriormente, eu fiz os testes sem o uso de passagem por referência e não deu certo, concluo que a mudança na versão 5 é para quando se usa o operador new e deseja atribuir o valor(objeto) para uma variavel com o operador '='. Me corrija se estiver errado.

 

Acho que nesse caso você tá usando HRP (Half Resposibility Principle) :grin:/>/>/>... haushasahsaus... Sim, acabei de inventar.

 

Você está distribuindo em duas classes o que poderia colocar em uma só... Basta você mover os métodos de BancoDeDados para CRUD ou vice-versa. Você ainda vai poder alterar a conexão se mantiver o método setConexao.

 

Eu sei que poderia colocar os métodos da classe BancoDeDados na CRUD ou vice-versa, funcionaria da mesma forma. Mas o principio SRP no meu ponto de vista seria violado, não? Imaginemos que a classe BancoDeDados foi transferida para CRUD. Se pensar nas responsabilidade desta classe agora, no meu ponto de vista, teriamos conexão com banco de dados e manipulação de registros, embora sejam coisas próximas, as responsabilidade são distinta. Se eu precisar mudar alguma coisa na conexão que o CRUD usa, vou precisar alterar a classe CRUD, e se precisar alterar alguma operação sobre o registros (deletar, consultar, atualizar e inserir) vou mexer na mesma classe, não seria o ideal, seria bom separar essas responsabilidades. Novamente me corrija se eu estiver equivocado.

Compartilhar este post


Link para o post
Compartilhar em outros sites

×

Informação importante

Ao usar o fórum, você concorda com nossos Termos e condições.