CRUD Genéricos em PHP
CRUD Genéricos em PHP
CRUD Genéricos em PHP
1 Parte: Create
criado por Marcio Alexandre em 02/05/2010 3:13pm Primeramente explicando o que CRUD: Create [criar], Retrieve [recuperar], Update [atualizar] e Delete [excluir]. Quero deixar registrado que o foco aqui no a performance do sistema, ok?! Afinal de contas, as 'tags' que caracterizam esse artigo bem especfico ;) Vamos iniciar, com este, um conjunto de 5 artigos explicando como fazer o famoso CRUD em php orientado objetos, de forma que no precisemos criar este conjunto de aes a quantidade de vezes que houver funes de acesso a base. Ou seja, nossos objetos estaro mais enxutos e no importando a regra de negcio, poderemos obter as informaes necessrias, puxadas no banco de dados, nas camadas acima, utilizando SEMPRE a mesma funo, modificando apenas os parmetros enviados. Basicamente, nas estruturas DAO, j temos os dados do objeto em questo. Como j sabemos para cada objeto temos a estrutura que receber cada dado referente sua tabela no banco. Alm disso temos tambm todas as aes deste objeto no sistema (inserts, updates, deletes, selects). Sabendo que este objeto tem um estrutura semelhando tabela do banco, podemos incrementar um pouco mais essas informaes afim de que ela se comunique com nossas classes genricas, e execute o que est sendo solicitado. Por questo de organizao e segurana, deixei as classes CRUD dentro do objeto de conexo com o banco. E nos objetos (DAO) ns importamos as classe e alimentamos os parmetros. Vamos l, trazendo o cdigo referente a um funcionrio (funcionarioDAO.php), j no padro de acesso s classes genricas:
class funcionario{ private $nome_tabela = 'tb_funcionario'; private $camposInsert = 'fun_nome,fun_data'; var $id_funcionario; var $fun_nome; var $fun_data; public function setDados($id_funcionario,$fun_nome,$fun_data){ if (isset($id_funcionario)){ $this->id_funcionario = $id_funcionario ; }else{$this->id_funcionario = null; } $this->fun_nome = $fun_nome ; $this->fun_data = $fun_data ; } public function getId(){ return $this->id_funcionario;} public function getNome() { return $this->fun_nome; } public function getDataBr() { /*modelo brasil (00/00/0000 00:00)*/ $temp = explode(' ',$this->fun_data); $temp_data = explode('',$temp[0]); $data = $temp_data[2].'/'.$temp_data[1].'/'.$temp_data[0]; $hora = $temp[1]; return $data.' '.$hora ; } public function getDataEn() { return $this->fun_data; } public function getNomeTabela() { return $this->nome_tabela; public function getNomeCampos() { return $this->camposInsert; } public function getValorCampos() { return "'".$this>fun_nome."','".$this->fun_data."' "; } }
public function Salvar($objeto,$db){ $db->insertObjectToDB($objeto); } public function Atualizar($objeto,$db){ $db>updateObjectToDB($objeto); } public function Pegar($fields,$values,$db){ return $db>selectDataDb($this->getNomeTabela(),$fields,$values);} public function PegarTodos($fields,$values,$db){ return $db>selectDatasDb($this->getNomeTabela(),$fields,$values); } public function Deletar($fields,$values,$db){ $db->DeleteDataDb($this>getNomeTabela(), $fields,$values); } }
Observe que adicionamos mais duas variveis privadas, no objeto, uma que traz o nome da tabela no banco, e a outra trazendo os fields (campos), em sequncia como utilizamos nos inserts em sql. Tambm inserimos duas funes para trazer os valores dessas duas variveis. Adicionamos tambm uma funo ( getVAlorCampos() ) que retorna uma string com alguns dados na mesma seqncia da varivel privada referente aos campos da tabela ($camposInsert), para inserir os dados. A outras variveis e funes so comuns aos DAOs. Na funo salvar ns enviamos o objeto em questo, j alimentado, e a varivel de referencia ao banco, que traz as classes genricas. Observe que necessrio um padro de nomenclaturas entre o objeto (nome dele mesmo) e o nome da tabela e seus campos. Para tornar o fluxo de informao vivel numa classe genrica temos que usar estes padres: Nome dos objetos: funcionarioDAO,clienteDAO,produtoDAO. Nome da classe:funcionario,cliente,produto (respectivamente) Nome das tabelas: tb_funcionario,tb_cliente,tb_produto (respectivamente). Nome das pginas: funcionario.php,cliente.php,produtos.php (respectivamente). Adotando este padro ser possvel acessar qualquer tabela baseada no objeto enviado, pois o nome ser lido a partir do objeto inserido na classe, e o fluxo automtico de pginas no sistema. Vejamos como a classe de insero construda, esta classe estar dentro do objeto banco.php (que traz consigo tambm a abertura de fechamento do banco Mysql):
class Banco{ private $local; private $user; private $senha; private $msg0; private $msg1; private $nome_db; private $db; public function __construct(){ $this->local = $this->user = $this->senha = $this->msg0 = $this->msg1 = de dados!'; $this->nome_db = }
'localhost'; 'root'; ''; 'Conexo falou, erro: '.mysql_error(); 'No foi possvel selecionar o banco 'db_sistemaqualquer';
public function abrir(){ $this->db = mysql_connect($this->local,$this->user,$this>senha) or die($this->msg0); mysql_select_db($this->nome_db,$this->db) or die($this->msg1); } public function fechar(){ //analisar se o mysql_close precisa ser colocado numa varivel $closed = mysql_close($this->db); $closed = NULL; } public function insertObjectToDB($objeto){ $db = new Banco(); $db->abrir(); $sql = "INSERT INTO ".$objeto->getNomeTabela()."(".$objeto>getNomeCampos().") VALUES ( ".$objeto->getValorCampos().")"; $query = mysql_query($sql) or die ($sql.' '.mysql_error()); $db->fechar(); unset($objeto); $temp_id = explode('_',$objeto->getNomeTabela()); header('location: '.$temp_id[1].'.php?msg=Inserido'); } } //class
Simples assim, a classe recebe via parmetro o objeto que quer inserir no banco. A string SQL montada automaticamente de acordo com os dados que j existem no objeto (neste caso sem wheres, mas nos prximos artigos mostraremos como montar as strings SQL com 1 ou mais wheres). E com os prprios dados que vem do objeto, obedecendo os padres de nomenclaturas adotados previamento j direciona para prxima pgina aps a insero. Agora o mais simples, o formulrio de insero:
<html> <head> <title>Cadastro de Funcionário</title> <meta http-equiv="Content-Type" content="text/html; charset=iso-88591"> </head> <body> <?php if (!$_POST){ $msg = @$_REQUEST['msg']; if (isset($msg)){ echo '<font color="red"> '.$msg.'</font><br><br>'; } $link = explode('/',$_SERVER ['REQUEST_URI']); $qtd = count($link); echo '<br>'; ?> <form action="<?php echo $link[$qtd-1]; ?>" method="post"> <table width="50%" border="0" cellspacing="0" cellpadding="0"> <tr> <td width="17%">Nome</td> <td width="49%"><input name="txtNome" type="text" id="txtNome" size="50"></td> </tr> <tr> <td> </td> <td> </td> </tr> <tr> <td> </td> <td><input type="submit" name="Submit" value="Enviar"></td> </tr>
<tr> <td> </td> <td> </td> </tr> </table> </form> <?php }else{ require('uses/banco.php'); $db = new Banco(); require('uses/funcionarioDAO.php'); $fun_nome = $_POST['txtNome']; $fun_data = date('Y-m-d H:i:s'); $func = new funcionario(); $func->setDados(null,$fun_nome,$fun_data); $func->salvar($func,$db); } ?> </body> </html>
Portanto, instanciamos o $func, onde alimentamos o objeto com os dados fornecidos pelo formulrio, e depois salvamos enviando ele mesmo ($func) e a instncia do objeto do banco ($db), via parmetro. S para clarear, utilizamos o $_SERVER ['REQUEST_URI'] para dar o refresh automtico, para no precisar se preocupar com mais este campo do formulrio, deixando mais dinmico. Bom, isso a, espero ter contribudo com uma programao mais prtica e sem frameworks pesados e muitas vezes desnecessrios. At a 2 Parte...
sua tabela no banco, ou seja, este objeto tem um estrutura semelhando tabela do banco. Por questo de organizao e segurana, deixei as classes CRUD dentro do objeto de conexo com o banco. E nos objetos (DAO) ns importaremos as classe e alimentamos os parmetros (aqui estar o segredo de manipulao das wheres). Vamos l, trazendo o cdigo referente a um funcionrio (funcionarioDAO.php), j no padro de acesso s classes genricas:
class funcionario{ private $nome_tabela = 'tb_funcionario'; private $camposInsert = 'fun_nome,fun_data'; var $id_funcionario; var $fun_nome; var $fun_data; public function setDados($id_funcionario,$fun_nome,$fun_data){ if (isset($id_funcionario)){ $this->id_funcionario = $id_funcionario ; }else{$this->id_funcionario = null; } $this->fun_nome = $fun_nome ; $this->fun_data = $fun_data ; } public function getId(){ return $this->id_funcionario;} public function getNome() { return $this->fun_nome; } public function getDataBr() { /*modelo brasil (00/00/0000 00:00)*/ $temp = explode(' ',$this->fun_data); $temp_data = explode('',$temp[0]); $data = $temp_data[2].'/'.$temp_data[1].'/'.$temp_data[0]; $hora = $temp[1]; return $data.' '.$hora ; } public function getDataEn() { return $this->fun_data; } public function getNomeTabela() { return $this->nome_tabela; public function getNomeCampos() { return $this->camposInsert; } public function getValorCampos() { return "'".$this>fun_nome."','".$this->fun_data."' "; } }
public function Salvar($objeto,$db){ $db->insertObjectToDB($objeto); } public function Atualizar($objeto,$db){ $db>updateObjectToDB($objeto); } public function Pegar($fields,$values,$db){ return $db>selectDataDb($this->getNomeTabela(),$fields,$values);} public function PegarTodos($fields,$values,$db){ return $db>selectDatasDb($this->getNomeTabela(),$fields,$values); } public function Deletar($fields,$values,$db){ $db->DeleteDataDb($this>getNomeTabela(), $fields,$values); } }
Observe que adicionamos mais duas variveis privadas, no objeto, uma que traz o nome da tabela no banco, e a outra trazendo os fields (campos), em sequncia como utilizamos nos inserts em sql. Tambm inserimos duas funes para trazer os valores dessas duas variveis. Tudo deve ser analisado, inclusive segurana. Utilizaremos, portanto, nas camadas superiores a funo Pegar. Explicando os parmetos, $fields e $values so vetores que trazem os campos e seus valores, com os quais filtraremos nossos dados no banco (refiro-me where). Aqui est um dos pontos principais do artigo, pois sendo assim, a where pode ser determinada pelo desenvolvedor sem problemas, e pode ser 01 ou mais filtros, basta informar campo
e valor, respectivamente, nos vetores. Aps enviar os parmetros para funo Pegar, a funo do banco selectDataDb as recebe, adicionando a funo da prpria classe getNomeTabela(), e retorna um valor (que iremos abordar mais adiante). Nos objetos, conforme vemos no code acima, sempre teremos a funo getNomeTabela() que retorna nossa varivel privada $nome_tabela, previamente alimentada com o nome da tabela no banco a que se refere. Isso torna cada objeto responsvel pela sua tabela no banco. No caso, o objeto funcionarioDAO.php consulta s, e somente s, a tabela tb_funcionario, e assim sucessivamente, como por exemplo clienteDAO.php acessaria somente a tabela tb_cliente. Observe a importncia de manter o nome da tabela nos demais arquivos. Ok. J explicamos a comunicao do objeto consigo mesmo, na chamada da funo Pegar. Agora vamos ver a comunicao da aplicao. Faremos aqui uma combo (HTML) bsica, alimentaremos o objeto funcionrioDAO com todos os dados do banco, passando os filtros, e imprimiremos o nome dele:
<html> <head> <title>Atualiza Funcionrio</title> <meta http-equiv="Content-Type" content="text/html; charset=iso-88591"> </head> <body> <?php require('uses/banco.php'); $db = new Banco(); require('uses/funcionarioDTO.php'); $fun = new funcionario(); if (!$_POST){ $fields[1] = 'id_funcionario'; $values[1] = $_REQUEST['id']; $objeto = $fun->Pegar($fields,$values,$db); ?> <input name="txtNome" type="text" id="txtNome" value="<?php echo $objeto->getNome(); ?>"> </body> </html>
At aqui nenhuma novidade. Importamos as classes banco e funcionario. E agora alimentaremos o vetor $fields e $value para filtrar os dados do select. Neste exemplo, estamos querendo trazer um funcionrio pelo id dele. Simples, informamos o campo e o valor, respectivamente. Lembro tambm que caberia aqui qualquer outra Field do banco, ok?! Nenhuma complicao. Agora vamos entrar na classe do banco e entender.
<?php class Banco{ private private private private private
private $nome_db; private $db; public function __construct(){ $this->local = 'localhost'; $this->user = 'root'; $this->senha = ''; $this->msg0 = 'Conexo falou, erro: '.mysql_error(); $this->msg1 = 'No foi possvel selecionar o banco de dados!'; $this->nome_db = 'db_exitosistemas'; } public function abrir(){ $this->db = mysql_connect($this->local,$this>user,$this->senha) or die($this->msg0); mysql_select_db($this->nome_db,$this->db) or die($this->msg1); } public function fechar(){ //analisar se o mysql_close precisa ser colocado numa varivel $closed = mysql_close($this->db); $closed = NULL; } public function BuscaValor($campo,$tb_name,$fields_where,$values_where){ $qtd = count($fields_where); if ($qtd != count($values_where)){ // verifica o numero de fields e values ?> <script language="javascript"> alert('Funo: selectDataDb. Erro: Quantidade de campos diferente da quantidade de valores'); </script> <?php }else{ // Monta a string SQL===================== $sql = "select ".$campo." from ".$tb_name; if ($qtd != 0 ) { for ($j=1;$j<=$qtd;$j++){ if ($j == 1) { $sql .= ' where '; } //garante que o where entre caso tenha algum parmetro $sql .= $fields_where[$j].' = '.$values_where[$j]; if ($j<$qtd ) { $sql .= ' and '; } } } //======================================= $res = mysql_query($sql) or die ($sql .mysql_error()); $linha = mysql_fetch_array($res); return $linha[$campo]; // retorna o valor do campo especifico, com os parametros enviados (podendo ser nenhum ou vrios) }//else }//public function public function selectDataDb($tb_name,$fields_where,$values_where){ // nome da tabela, vetores: campos(fields), valores(values) da consulta $obj = explode('_',$tb_name); $new_objeto = new $obj[1]; $db = new Banco();
$db->abrir(); $fields = mysql_list_fields('db_tabelaqualquer',$tb_name); // informa os fields/campos da tabela do banco $columns = mysql_num_fields($fields); //conta o nmero de campos for ($i = 0; $i < $columns; $i++) { $fields_name = mysql_field_name($fields, $i); $fields_values = $this>BuscaValor(mysql_field_name($fields, $i),$tb_name,$fields_where,$values_where); $new_objeto->$fields_name = $fields_values; } $db->fechar(); return $new_objeto; } ?>
Ok! Aqui est a classe. No se assuste pelo tamanho e pela quantidade de laos de repetio. Vamos entender...(eu fiz alguns comentrios no cdigo, talvez ajude). A funo selectDataDb() recebe os parmetros enviados pela funo Pegar em FuncionarioDAO. Simples assim! Nenhuma novidade... Sabemos que ao acessar o banco, precisamos criar um objeto de retorno de dados (obedecendo ao Data Access Object). Sendo assim, minha classe tem que entender qual objeto ser carregado aps o select no banco. Como fazer minha funo entender qual objeto o banco ir popular? Agora vai a importncia de manter a nomenclatura da tabela do nosso banco, nas demais partes do sistema (nome do objeto, da classe, e por a vai...). Ao acessar a funo selectDataDb(), o nome da tabela quebrada (ultizando o explode do php), para s ento, o sistema entender qual objeto ser carregado.
$obj = explode('_',$tb_name); $new_objeto = new $obj[1];
Nisso tornamos genrica nossa classe. Qualquer objeto pode ser carregado. E sabendo que o objeto carrega em si o nome da prpria tabela a que se refere, automtico. Aps isso, ns adentramos e colhemos no banco, quais e quantas fields que l existe, fisicamente. Baseado na quantidade de campos existente, fazemos um lao, para buscar o nome do campo e o valor deste campo. Buscar o nome simples, utilizaremos a funo do mysql: mysql_field_name. Onde informamos a posio da Field. Para entender melhor: se numa tabela eu tenho nome,endereco,contato,sexo, nome seria a posio: 1, endereco: 2, contato: 3, e assim sucessivamente.
$fields = mysql_list_fields('db_tabelaqualquer',$tb_name); // informa os fields/campos da tabela do banco $columns = mysql_num_fields($fields); //conta o nmero de campos for ($i = 0; $i < $columns; $i++) { $fields_name = mysql_field_name($fields, $i); $fields_values = $this-
Para buscar o valor, precisaremos de uma funo de auxlio. Seno o cdigo fica muito complicado. E aqui que fica a construo de nossa SQL Dinmica. Envia-se o nome do campo desejado, o nome da tabela, e os vetores que construiro nossas wheres.
$this->BuscaValor(mysql_field_name($fields, $i),$tb_name,$fields_where,$values_where);
Entraremos ento na funo BuscaValor. Logo na entrada cuidaremos de nosso filtro do select. Quantas clusulas formaro a where? necessrio saber, e j tratamos isso logo no incio.
$qtd = count($fields_where);
Caso esteja tudo ok. Prosseguimos para a construo real do select. Um coisa linda (me empolguei... hehehehehe).
// Monta a string SQL===================== $sql = "select ".$campo." from ".$tb_name; if ($qtd != 0 ) { for ($j=1;$j<=$qtd;$j++){ if ($j == 1) { $sql .= ' where '; } //garante que o where entre caso tenha algum parmetro $sql .= $fields_where[$j].' = '.$values_where[$j]; if ($j<$qtd ) { $sql .= ' and '; } } }
A varivel $sql recebe o texto concatenado com os novos valores das variveis. O IF garante que nossa where receber algo para ser trabalhado. O lao de repetio varre o vetor, at a quantidade de campos e valores existe no vetor, trazendo TODOS os que fora passados. O primeiro IF depois do For, garante que o a palavra where seja concatenada na string $sql, e logo aps concatenado os campos e valores respectivamente. O prximo IF somente para concatenar a palavra and string citada, caso haja mais campos e valores a serem concatenados. Nenhum segredo at ento. s substituir o nomes que costumeiramente usando nos SQL de consulta, por variveis que recebero os valores em tempo de execuo. E nisso
consiste a idia principal do artigo. Bom pessoal, sou Marcio Alexandre. Encerro aqui mais este artigo. Talvez tenha ficado algo confuso, devido ao tamanho. Qualquer dvida, s entrar em contato. E mais uma vez, agradeo a ateno, e espero ter contribudo com mais este conhecimento comunidade OO PHP. =) Prximo artigo: Retrieve (com retorno de um list de objetos populados). =)
$this->fun_data = $fun_data ; } public function getId(){ return $this->id_funcionario;} public function getNome() { return $this->fun_nome; } public function getDataBr() { /*modelo brasil (00/00/0000 00:00)*/ $temp = explode(' ',$this->fun_data); $temp_data = explode('',$temp[0]); $data = $temp_data[2].'/'.$temp_data[1].'/'.$temp_data[0]; $hora = $temp[1]; return $data.' '.$hora ; } public function getDataEn() { return $this->fun_data; } public function getNomeTabela() { return $this->nome_tabela; public function getNomeCampos() { return $this->camposInsert; } public function getValorCampos() { return "'".$this>fun_nome."','".$this->fun_data."' "; } }
public function Salvar($objeto,$db){ $db->insertObjectToDB($objeto); } public function Atualizar($objeto,$db){ $db>updateObjectToDB($objeto); } public function Pegar($fields,$values,$db){ return $db>selectDataDb($this->getNomeTabela(),$fields,$values);} public function PegarTodos($fields,$values,$db){ return $db>selectDatasDb($this->getNomeTabela(),$fields,$values); } public function Deletar($fields,$values,$db){ $db->DeleteDataDb($this>getNomeTabela(), $fields,$values); } }
Observe que adicionamos mais duas variveis privadas, no objeto, uma que traz o nome da tabela no banco, e a outra trazendo os fields (campos), em sequncia como utilizamos nos inserts em sql. Tambm inserimos duas funes para trazer os valores dessas duas variveis. Tudo deve ser analisado, inclusive segurana. Utilizaremos, portanto, nas camadas superiores a funo PegarTodos. Explicando os parmetos, $fields e $values so vetores que trazem os campos e seus valores, com os quais filtraremos nossos dados no banco (refiro-me where). Aqui est um dos pontos principais do artigo, pois sendo assim, a where pode ser determinada pelo desenvolvedor sem problemas, e pode ser 01 ou mais filtros, basta informar campo e valor, respectivamente, nos vetores. Aps enviar os parmetros para funo PegarTodos, a funo do banco selectDatasDb as recebe, adicionando a funo da prpria classe getNomeTabela(), e retorna um valor (que iremos abordar mais adiante). Nos objetos, conforme vemos no exemplo acima, sempre teremos a funo getNomeTabela() que retorna nossa varivel privada $nome_tabela, previamente alimentada com o nome da tabela no banco a que se refere. Isso torna cada objeto responsvel pela sua tabela no banco. No caso, o objeto funcionarioDAO.php consulta s, e somente s, a tabela tb_funcionario, e assim sucessivamente, como por exemplo clienteDAO.php acessaria somente a tabela tb_cliente. Observe a importncia de manter o nome da tabela nos demais arquivos. Ok. J explicamos a comunicao do objeto consigo mesmo, na chamada da funo PegarTodos. E vimos que at agora no existe qualquer diferena com o artigo anterior de pegar apenas um objeto em especfico.
Agora vamos ver a comunicao da aplicao com o objeto. Faremos aqui uma combo (HTML) bsica, alimentaremos o objeto funcionrioDAO com todos os dados do banco, passando os filtros, e traremos todos eles para nossa aplicao:
<html> <body> <?php require('uses/banco.php'); $db = new Banco(); ?> <select name="id_funcionario"> <?php require('uses/funcionario_.php'); $func = new funcionario(); $objeto = $func->PegarTodos($fields,$values,$db); foreach($objeto as $m => $obj){ ?> <option value="<?php echo $obj->getId(); ?>"><?php echo $obj>getNome(); ?></option> <?php } ?> </select> </body> </html>
At aqui nenhuma novidade. Importamos as classes banco e funcionario. Agora alimentaremos o vetor $fields e $value para filtrar os dados do select dentro do objeto. Neste exemplo, estamos querendo trazer todos os funcionrios existente na base, por isso os fields e values esto vazios, porque no precisa de where especfica. Lembro tambm que caberia aqui QUALQUER filtro, caso houvesse necessidade, ok?! Sem nenhum problema. Agora vamos entrar na classe do banco e entender. extremamente parecido com o artigo anterior, porm agora estamos trazendo um list de objetos.
<?php class Banco{ private $local; private $user; private $senha; private $msg0; private $msg1; private $nome_db; private $db; public function __construct(){ $this->local = 'localhost'; $this->user = 'root'; $this->senha = ''; $this->msg0 = 'Conexo falou, erro: '.mysql_error(); $this->msg1 = 'No foi possvel selecionar o banco de dados!'; $this->nome_db = 'db_exitosistemas'; } public function abrir(){ $this->db = mysql_connect($this->local,$this>user,$this->senha) or die($this->msg0); mysql_select_db($this->nome_db,$this->db) or die($this->msg1); }
public function fechar(){ //analisar se o mysql_close precisa ser colocado numa varivel $closed = mysql_close($this->db); $closed = NULL; } public function BuscaValor( $campo, $tb_name, $fields_where, $values_where ){ $qtd = count($fields_where); if ($qtd != count($values_where)){ // verifica o numero de fields e values ?> <script language="javascript"> alert('Funo: selectDataDb. Erro: Quantidade de campos diferente da quantidade de valores'); </script> <?php }else{ // Monta a string SQL===================== $sql = "select ".$campo." from ".$tb_name; if ($qtd != 0 ) { for ($j=1;$j<=$qtd;$j++){ if ($j == 1) { $sql .= ' where '; }//garante que o where entre caso tenha algum parametro $sql .= $fields_where[$j].' = '.$values_where[$j]; if ($j<$qtd ) { $sql .= ' and '; } } } //======================================= $res = mysql_query($sql) or die ($sql .mysql_error()); $linha = mysql_fetch_array($res); return $linha[$campo]; // retorna o valor do campo especifico, com os parametros enviados (podendo ser nenhum ou vrios) }//else }//public function public function selectDatasDb( $tb_name, $fields_where, $values_where){ // nome da tabela, vetores: campos(fields), valores(values) da consulta $obj = explode('_',$tb_name); $qtd = count($fields_where); $db = new Banco(); $db->abrir(); // <Monta a string SQL> ===================== $sql = "SELECT id_".$obj[1]." FROM ".$tb_name; if ($qtd >= 1 ) { for ($j=1;$j<=$qtd;$j++){ if ($j == 1) { $sql .= ' WHERE '; }//garante que o where entre caso tenha algum parametro $sql .= $fields_where[$j].' = '.$values_where[$j]; if ($j<$qtd ) { $sql .= ' AND '; } } } //==================== </Monta a string SQL> $query = mysql_query($sql) or die ($sql .mysql_error()); $h = 0; // inicializamos a varivel
// Lao que trar TODOS os objetos j alimentados while ($linha = mysql_fetch_array($query)){ $objDTO = new $obj[1]; $fields_where[$qtd+1] = 'id_'.$obj[1]; $values_where[$qtd+1] = $linha[$fields_where[$qtd+1]]; $fields = mysql_list_fields('db_exitosites',$tb_name); // informa os fields/campos da tabela do banco $columns = mysql_num_fields($fields); //conta o nmero de campos $new_objeto[$h] = clone $objDTO; //Aqui declaramos um novo objeto que clone de nosso DAO, no momento sendo apenas um DTO. for ($i = 0; $i < $columns; $i++) { $fields_name = mysql_field_name($fields, $i); $fields_values = $this>BuscaValor(mysql_field_name($fields, $i),$tb_name,$fields_where,$values_where); $new_objeto[$h]->$fields_name = $fields_values; } $h += 1; //chamamos o prximo objeto } //fecha o while, terminando nosso vetor de objetos. $db->fechar(); return $new_objeto; //retorna todos os objetos populados } ?>
Ok! Aqui est a to temida classe. No se assuste pelo tamanho e pela quantidade de laos de repetio (mais do que do artigo anterior). Vamos entender...(eu fiz alguns comentrios no cdigo, talvez ajude). A funo selectDatasDb() recebe os parmetros enviados pela funo PegarTodos em FuncionarioDAO. Simples assim! Nenhuma novidade... Sabemos que ao acessar o banco, precisamos criar um objeto de retorno de dados (obedecendo ao Data Access Object). Sendo assim, minha classe tem que entender qual objeto ser carregado aps o select no banco. Como fazer minha funo entender qual objeto o banco ir popular? Agora vai a importncia de manter a nomenclatura da tabela do nosso banco, nas demais partes do sistema (nome do objeto, da classe, e por a vai...). Ao acessar a funo selectDatasDb(), o nome da tabela quebrada (ultizando o explode do php), para s ento, o sistema entender qual objeto ser carregado.
$obj = explode('_',$tb_name); $new_objeto = new $obj[1];
Nisso tornamos genrica nossa classe. Qualquer objeto pode ser carregado. E sabendo que o objeto carrega em si o nome da prpria tabela a que se refere, automtico. Aqui montaremos nosso select para saber os registros (linhas) que existem no banco, e colhemos o id de cada um, para s ento colhermos os fields e seus valores, e posteriormente popular o objeto:
// <Monta a string SQL> ===================== $sql = "SELECT id_".$obj[1]." FROM ".$tb_name; if ($qtd >= 1 ) { for ($j=1;$j<=$qtd;$j++){ if ($j == 1) { $sql .= ' WHERE '; }//garante que o where entre caso tenha algum parametro $sql .= $fields_where[$j].' = '.$values_where[$j]; if ($j<$qtd ) { $sql .= ' AND '; } } } //==================== </Monta a string SQL>
Aps isso, enquanto houver registro no banco (linhas ou tuplas), que ns determinamos no while:
while ($linha = mysql_fetch_array($query)){
ento varremos os dados de cada linha, lembrando que alimentamos mais uma vez os fields e os values dinamicamente. Afinal precisamos dizer qual registro precisamos obter, e passamos por parmetro o id colhido no select acima:
$fields_where[$qtd+1] = 'id_'.$obj[1]; $values_where[$qtd+1] = $linha[$fields_where[$qtd+1]];
Buscar o nome simples, utilizaremos a funo do mysql: mysql_field_name. Onde informamos a posio da Field. Para entender melhor: se numa tabela eu tenho nome,endereco,contato,sexo, nome seria a posio: 1, endereco: 2, contato: 3, e assim sucessivamente.
$fields = mysql_list_fields('db_exitosites',$tb_name); // informa os fields/campos da tabela do banco $columns = mysql_num_fields($fields); //conta o nmero de campos $new_objeto[$h] = clone $objDTO; //Aqui declaramos um novo objeto que clone de nosso DAO, no momento sendo apenas um DTO. for ($i = 0; $i < $columns; $i++) { $fields_name = mysql_field_name($fields, $i); $fields_values = $this->BuscaValor( mysql_field_name( $fields, $i),$tb_name,$fields_where,$values_where); $new_objeto[$h]->$fields_name = $fields_values; //aqui populamos com todos os campos e valores o objeto. }
Para buscar o valor, precisaremos de uma funo de auxlio. Seno o cdigo fica muito complicado. E aqui que fica a construo de nossa SQL Dinmica. Desta vez o nosso for ficar dentro de outro lao de repetio while. Que popular a quantidade de objetos referente s tublas da tabela no banco. Conforme j explicado ! Envia-se o nome do campo desejado, o nome da tabela, e os vetores que construiro nossas wheres.
$this->BuscaValor(mysql_field_name($fields, $i),$tb_name,$fields_where,$values_where);
Entraremos ento na funo BuscaValor. Logo na entrada cuidaremos de nosso filtro do select. Quantas clusulas formaro a where? necessrio saber, e j tratamos isso logo no incio.
$qtd = count($fields_where);
A varivel $sql recebe o texto concatenado com os novos valores das variveis. O IF garante que nossa where receber algo para ser trabalhado. O lao de repetio varre o vetor, at a quantidade de campos e valores existe no vetor, trazendo TODOS os que fora passados. O primeiro IF depois do For, garante que o a palavra where seja concatenada na string $sql, e logo aps concatenado os campos e valores respectivamente. O prximo IF somente para concatenar a palavra and string citada, caso haja mais campos e valores a serem concatenados. Nenhum segredo at ento. s substituir o nomes que costumamente usando nos SQL de consulta, por variveis que recebero os valores em tempo de execuo. E nisso consiste a idia principal do artigo. A funo 'BuscaValor' idntica a utilizada na funo do artigo anterior, que traz apenas um objeto populado. A modificao (entre trazer um objeto ou um vetor com todos os objetos) aqui apenas dentro do SelectDatasDb, que varre todos os registros existentes no banco. Ainda dentro do SelectDatasDb, para concluir os processos, fechamos o banco e retornamos o vetor com todos os objetos populados:
$db->fechar(); return $new_objeto; //retorna todos os objetos populados
Pronto! Um pouco complexo, mas factvel. =) O Desempenho aqui deve cair, mas temos uma grande vantagem, na hora da manuteno no precisamos cair nas funes dento do objeto do banco. Basta nos preocupar com o carregamento do objeto desejado e alimentao dos vetores que faro as 'wheres'. Percebam que a regra de negcio fica numa camada muito acima, e numa forma de comunicao nada trivial. Bom pessoal, sou Marcio Alexandre. Encerro aqui mais este artigo. Talvez tenha ficado algo ainda mais confuso, devido ao tamanho. Qualquer dvida, s entrar em contato. E mais uma vez, agradeo a ateno, e espero ter contribudo com mais este conhecimento comunidade OO PHP. Prximo artigo: Update. =)
var $fun_nome; var $fun_data; public function setDados($id_funcionario,$fun_nome,$fun_data){ if (isset($id_funcionario)){ $this->id_funcionario = $id_funcionario ; }else{$this->id_funcionario = null; } $this->fun_nome = $fun_nome ; $this->fun_data = $fun_data ; } public function getId(){ return $this->id_funcionario;} public function getNome() { return $this->fun_nome; } public function getDataBr() { /*modelo brasil (00/00/0000 00:00)*/ $temp = explode(' ',$this->fun_data); $temp_data = explode('',$temp[0]); $data = $temp_data[2].'/'.$temp_data[1].'/'.$temp_data[0]; $hora = $temp[1]; return $data.' '.$hora ; } public function getDataEn() { return $this->fun_data; } public function getNomeTabela() { return $this->nome_tabela; public function getNomeCampos() { return $this->camposInsert; } public function getValorCampos() { return "'".$this>fun_nome."','".$this->fun_data."' "; } }
public function Salvar($objeto,$db){ $db->insertObjectToDB($objeto); } public function Atualizar($objeto,$db){ $db>updateObjectToDB($objeto); } public function Pegar($fields,$values,$db){ return $db>selectDataDb($this->getNomeTabela(),$fields,$values);} public function PegarTodos($fields,$values,$db){ return $db>selectDatasDb($this->getNomeTabela(),$fields,$values); } public function Deletar($fields,$values,$db){ $db->DeleteDataDb($this>getNomeTabela(), $fields,$values); } }
Observe que adicionamos mais duas variveis privadas, no objeto, uma que traz o nome da tabela no banco, e a outra trazendo os fields (campos), em sequncia como utilizamos nos inserts em sql. Tambm inserimos duas funes para trazer os valores dessas duas variveis. Tudo deve ser analisado, inclusive segurana. Utilizaremos, portanto, nas camadas superiores a funo Atualizar. Nos artigos anteriores eu expliquei os parmetos, $fields e $values que seriam vetores que trazem os campos e seus valores, com os quais filtraramos nossos dados no banco (refiro-me where). Estraramos aqui um dos pontos principais dos artigos anteriores, pois sendo assim, a where poderia ser determinada pelo desenvolvedor sem problemas, e poderia ser 01 ou mais filtros, bastando informar campo e valor, respectivamente, nos vetores. Neste artigo no precisamos abordar os campos e seus valores. Basicamente, o que teremos uma lista de objetos sendo exibida na tela (no caso, sero os funcionrios, para exemplificar) e ao clicar em uma opo, carregaremos de imediato aquele objeto (utilizando a funo Pegar, conforme j entendemos no segundo artigo desta srie). Aps carregar nosso objeto, exibimos os dados no HTML, alteramos o que achamos necessrio, e o retornamos para a funo Atualizar da classe funcionrio. Pergunta provvel: Como o banco vai saber qual tupla atualizar, onde est o id? Ao ser exibida a lista de funcionrios, o link para alter-lo dever enviar o id do objeto
escolhido, ou enviar via hidden. Muito simples de se entender, concorda? Na pgina de exibio dos dados a serem alterados, teramos a seguinte estrutura:
//instancio o banco, onde tem minhas funes genricas require('uses/banco.php'); $db = new Banco(); //Instancio minha classe funcionario require('uses/funcionario_.php'); $fun = new funcionario(); //Aqui digo que se no for um post, ento para exibir os dados do funcionrio escolhido if (!$_POST){ //verifica se uma submisso de formulrio ou no // recebo o id do funcionario $fields[1] = 'id_funcionario'; $values[1] = $_REQUEST['id']; $objeto = $fun->Pegar($fields,$values,$db);
<?php }else{ //buscando os dados REQUEST $id_funcionario = $_POST['id_funcionario']; if (!isset($id_funcionario) || $id_funcionario != ''){ $fun_nome = $_POST['txtNome'] ; $fun_data = $_POST['fun_data']; //inserindo no objeto $fun->id_funcionario = $id_funcionario; $fun->fun_nome = $fun_nome; $fun->fun_data = $fun_data; //chamando funo para envio dos dados dinmicamente $fun->Atualizar($fun,$db); }else{ ?> <script language="javascript"> alert('Identificador do contedo no especificado'); location.href='index.php'; </script> <?php } // else do if (!isset($id_funcionario) ... } // else do if (!$_POST){ ?>
Como vimos nos artigos anteriores... a funo/mtodo Atualizar da classe Funcionario, evoca o mtodo updateObjectToDBdo banco enviado, enviando o objeto j populado. Como no haver retorno (o que poderia, a critrio do programador, trazer um true ou false, correspondendo a algum critrio de controle de try - catch, enfim, possibilidades so inmeras). Pronto! Entendido estas arestas que no tratei na poca...vamos ao corao deste artigo! Vamos entrar na classe do banco e entender. As explicaes vem no cdigo, e de fcil entendimento considerando o nome intuitivo dos mtodos.
### Updater public function updateObjectToDB($objeto){ //15abr2010 // Processando, em execuo, a obteno de dados do objeto passado! $temp_id = explode('_',$objeto>getNomeTabela()); $id_nome = 'id_'.$temp_id[1]; //criando varivel que ser representar o id do objeto passado. Aqui vlido informar que h uma padronizao no banco de dados em todas as tabelas, no que se refere nomenclatura $temp_campos = explode(',',$objeto>getNomeCampos()); // Aqui teremos os nomes dos campos da tabela, conforme foi alimentado no objeto. $temp_valores = explode(',',$objeto>getValorCampos()); // Novos dados (ou no) que sero substituidos no banco. Vale lembrar que todos os campos sero substitudo, por mais que os dados novos e antigos sejam os mesmos. Um brecha aqui para futuras melhorias, para no perder no quesito processo. $qtd_campos = count($temp_campos); //Varivel que armazenar a qtd de campos, afim de nortear a criao dinmica da string SQL. //MONTANDO STRING SQL Dinamicamente-------------------
------------$new_string = 'UPDATE '.$objeto->getNomeTabela().' set '; for ($i=0;$i <= $qtd_campos-1;$i++){ // concatenao de valores $new_string .= $temp_campos[$i].' = '.$temp_valores[$i]; if ($i < $qtd_campos-1){ $new_string .= ','; } } // concatena a SQL com a clusula where. $new_string .= ' WHERE '.$id_nome.' = '.$objeto>getId().''; //---------------------------------------------------//echo $new_string; break; // caso queria imprimir a string SQL para algum teste (e somente para isto), desabilitar. //abre o banco submete mysql_query e fecha $db = new Banco(); $db->abrir(); $sql = $new_string; $query = mysql_query($sql) or die ($sql.' '.mysql_error()); $db->fechar(); //Redirecionando para pgina de atualizao referida header('Location:'.$temp_id[1].'_update.php?id='.$ objeto->getId().'&msg=Atualizado'); }
Bom, isso!! Espero que seja til comunidade, a inteno somente esta. * Estou pensando em hospedar estes codes em algum site controlador de verses, que trabalhe com open source. A ideia construmos, juntos, esta camada CRUD, baseada em collections (arrays), de maneira simples e segura. Isto pode facilitar muito a vida de alguns analistas/programadores, que prefiram trabalhar com SQL Dinmicas, e no fazer mais 'n' funes para gerenciar o fluxo de dados entre o banco e a aplicao, e vice-versa. Acredito que com esta despretensiosa soluo, podemos atender melhor (quesito: tempo) a gerencia de requisitos e sua manipulao, atendendo inclusive aos conceitos de "metodologias agis", pois TODA aplicao estaria em cima de cinco funes somente, ou seja, qualquer alterao ou nova implementao, seria rpida e facilmente gerencivel.