+3

CRUD Genéricos em PHP. 2ª Parte: Retrieve (de um objeto)

criado por Marcio Alexandre em 17/06/2010 1:56pm
Mais uma vez vamos nós, utilizando o mesmo principio de antes, sem foco no desempenho, e utilização de SQL dinâmicas para suportar o conceito genérico em OO PHP.

Observando os mesmo conceitos da parte ‘CREATE’ destes 5 artigos. Continuemos agora com o Retrieve (ou as famosas ‘consultas’).

Atenção: Este artigo se referente às consultas de um determinado dado que alimentará um objeto, e não o retorno de uma lista de objetos (isso ficará para o próximo artigo, porque tornar possível a leitura de qualquer table, e ‘varrer’ suas possíveis fields, é realmente muito complicado, achei melhor dividir).

Tornarei a repetir algumas conceitos teóricos, para deixar os artigos independentes.

Como já sabemos, para cada objeto temos a estrutura que receberá cada dado referente à sua tabela no banco, ou seja, este objeto tem um estrutura semelhando à tabela do banco.

Por questão de organização e segurança, deixei as classes CRUD dentro do objeto de conexão com o banco. E nos objetos (DAO) nós importaremos as classe e alimentamos os parâmetros (aqui estará o segredo de manipulação das wheres).

Vamos lá, trazendo o código referente a um funcionário (funcionarioDAO.php), já no padrão de acesso às classes genéricas:

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 variáveis privadas, no objeto, uma que traz o nome da tabela no banco, e a outra trazendo os fields (campos), em sequência como utilizamos nos inserts em sql. Também inserimos duas funções para trazer os valores dessas duas variáveis. Tudo deve ser analisado, inclusive segurança.

Utilizaremos, portanto, nas camadas superiores a função ‘Pegar’.
Explicando os parâmetos, ‘$fields’ e ‘$values’ são 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.

Após enviar os parâmetros para função Pegar, a função do banco selectDataDb as recebe, adicionando a função da própria classe getNomeTabela(), e retorna um valor (que iremos abordar mais adiante).

Nos objetos, conforme vemos no code acima, sempre teremos a função getNomeTabela() que retorna nossa variável privada $nome_tabela, previamente alimentada com o nome da tabela no banco a que se refere. Isso torna cada objeto responsável 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 importância de manter o nome da tabela nos demais arquivos.

Ok. Já explicamos a comunicação do objeto consigo mesmo, na chamada da função Pegar. Agora vamos ver a comunicação da aplicação.
Faremos aqui uma combo (HTML) básica, alimentaremos o objeto funcionárioDAO com todos os dados do banco, passando os filtros, e imprimiremos o nome dele:

<html>
<head>
<title>Atualiza Funcionário</title>
<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">
</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 funcionário pelo id dele. Simples, informamos o campo e o valor, respectivamente. Lembro também que caberia aqui qualquer outra Field do banco, ok?! Nenhuma complicação.

Agora vamos entrar na classe do banco e entender.

<?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  	=	'Conexão falou, erro: '.mysql_error();
		$this->msg1  	=	'Não foi possível 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 variável
		$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('Função: 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 parâmetro
				$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 vários)
		}//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 número 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. Não se assuste pelo tamanho e pela quantidade de laços de repetição. Vamos entender...(eu fiz alguns comentários no código, talvez ajude).

A função selectDataDb() recebe os parâmetros enviados pela função 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 após o select no banco. Como fazer minha função entender qual objeto o banco irá popular? Agora vai a importância 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 função selectDataDb(), o nome da tabela é ‘quebrada’ (ultizando o explode do php), para só então, o sistema entender qual objeto será carregado.

		$obj = explode('_',$tb_name);
		$new_objeto = new $obj[1];

Nisso tornamos genérica nossa classe. Qualquer objeto pode ser carregado. E sabendo que o objeto carrega em si o nome da própria tabela a que se refere, é automático.

Após isso, nós adentramos e colhemos no banco, quais e quantas fields que lá existe, ‘fisicamente’. Baseado na quantidade de campos existente, fazemos um laço, para buscar o nome do campo e o valor deste campo.
Buscar o nome é simples, utilizaremos a função do mysql: mysql_field_name. Onde informamos a posição da Field. Para entender melhor: se numa tabela eu tenho ‘nome,endereco,contato,sexo’, nome seria a posição: 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 número 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;
		}

Para buscar o valor, precisaremos de uma função de auxílio. Senão o código fica muito complicado. E é aqui que fica a construção de nossa SQL Dinâmica.

Envia-se o nome do campo desejado, o nome da tabela, e os vetores que construirão nossas wheres.

$this->BuscaValor(mysql_field_name($fields, $i),$tb_name,$fields_where,$values_where);

Entraremos então na função BuscaValor.
Logo na entrada cuidaremos de nosso filtro do select. Quantas cláusulas formarão a where? É necessário saber, e já tratamos isso logo no início.

		$qtd = count($fields_where);

Validamos se a quantidade de fields e seus valores correspondem no IF abaixo.

		if ($qtd != count($values_where)){ // verifica o numero de fields e values ?>
			<script language="javascript">
				alert('Função: selectDataDb. Erro: Quantidade de campos diferente da quantidade de valores');
		</script>
	<?php  }else{

Caso esteja tudo ok. Prosseguimos para a construção 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 parâmetro
				$sql .= $fields_where[$j].' = '.$values_where[$j];		
				if ($j<$qtd )  { $sql .= ' and '; }
		}
		}

A variável $sql recebe o texto concatenado com os novos valores das variáveis. O IF garante que nossa where receberá algo para ser trabalhado. O laço de repetição 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 após é concatenado os campos e valores respectivamente. O próximo IF é somente para concatenar a palavra ‘and’ à string citada, caso haja mais campos e valores a serem concatenados.

Nenhum segredo até então. É só substituir o nomes que costumamente é usando nos SQL de consulta, por variáveis que receberão os valores em tempo de execução. E nisso consiste a idéia principal do artigo.

Bom pessoal, sou Marcio Alexandre. Encerro aqui mais este artigo. Talvez tenha ficado algo confuso, devido ao tamanho. Qualquer dúvida, é só entrar em contato.

E mais uma vez, agradeço a atenção, e espero ter contribuído com mais este conhecimento à comunidade OO PHP. =)

Próximo artigo: Retrieve (com retorno de um list de objetos populados). =)

Comentários:

Mostrando 1 - 4 de 4 comentários
Marcio seu artigo esta demais.
No entanto recebo o seguinte erro

Fatal error: Class name must be a valid object or a string in /home....

on line 61

que se refere a

$obj = explode('_',$tb_name);
$new_objeto = new $obj[1];


Podem me dar uma luz do por que recebo essa mensagem? obrigado.
20/06/2013 8:18pm (~10 anos atrás)

Bom dia!

Temos em algum local estes exemplo funcionando ?
27/09/2011 8:39am (~12 anos atrás)

Link para o artigo posterior-> Retrieve (trazendo uma coleção de objetos): http://www.phpbrasil.com/artigo/ajN7IQp9AcZd/crud-genericos-em-php-3a-parte-retrieve-de-varios-objetos
28/03/2011 2:58pm (~13 anos atrás)

Novo Comentário:

(Você pode usar tags como <b>, <i> ou <code>. URLs serão convertidas para links automaticamente.)