Construindo CRUD genérico com PHP e PDO
Vantagens em usar um CRUD genérico
Sempre que trabalho com CRUD no PHP utilizo a biblioteca PDO por vários motivos, aqui no blog já postei vários artigos sobre PDO demonstrando suas vantagens e principais características. Essa classe foi testada com sucesso apenas com o SGBD MySQL, SQL Server 2008 e PostgreSQL, mas acredito que não haverá problemas com outros banco de dados uma vez que o PDO é extremamente portável para esse tipo de mudança e as instruções SQL são simples não envolvendo características particulares de nenhum tipo de SGBD, facilitando a ideia de um CRUD genérico.
Abaixo vou postar a classe e em seguida explico a funcionalidade de cada método com detalhes:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 |
<?php /*************************************************************************************************************** * @author William F. Leite * * Data: 20/06/2014 * * Título: CRUD genérico * * Descrição: A Classe de CRUD genérico foi elaborada com o objetivo de auxlilar nas operações CRUDs em diversos* * SGBDS, possui funcionalidades para construir instruções de INSERT, UPDATE E DELETE onde as mesmas podem ser * * executadas nos principais SGBDs, exemplo SQL Server, MySQL e Firebird. Instruções SELECT são recebidas * * integralmente via parâmetro. * *************************************************************************************************************/ header('Content-Type: text/html; charset=utf-8'); class Crud{ // Atributo para guardar uma conexão PDO private $pdo = null; // Atributo onde será guardado o nome da tabela private $tabela = null; // Atributo estático que contém uma instância da própria classe private static $crud = null; /* * Método privado construtor da classe * @param $conexao = Conexão PDO configurada * @param $tabela = Nome da tabela */ private function __construct($conexao, $tabela=NULL){ if (!empty($conexao)): $this->pdo = $conexao; else: echo "<h3>Conexão inexistente!</h3>"; exit(); endif; if (!empty($tabela)) $this->tabela =$tabela; } /* * Método público estático que retorna uma instância da classe Crud * @param $conexao = Conexão PDO configurada * @param $tabela = Nome da tabela * @return Atributo contendo instância da classe Crud */ public static function getInstance($conexao, $tabela=NULL){ // Verifica se existe uma instância da classe if(!isset(self::$crud)): try { self::$crud = new Crud($conexao, $tabela); } catch (Exception $e) { echo "Erro " . $e->getMessage(); } endif; return self::$crud; } /* * Método para setar o nome da tabela na propriedade $tabela * @param $tabela = String contendo o nome da tabela */ public function setTableName($tabela){ if(!empty($tabela)){ $this->tabela = $tabela; } } /* * Método privado para construção da instrução SQL de INSERT * @param $arrayDados = Array de dados contendo colunas e valores * @return String contendo instrução SQL */ private function buildInsert($arrayDados){ // Inicializa variáveis $sql = ""; $campos = ""; $valores = ""; // Loop para montar a instrução com os campos e valores foreach($arrayDados as $chave => $valor): $campos .= $chave . ', '; $valores .= '?, '; endforeach; // Retira vírgula do final da string $campos = (substr($campos, -2) == ', ') ? trim(substr($campos, , (strlen($campos) - 2))) : $campos ; // Retira vírgula do final da string $valores = (substr($valores, -2) == ', ') ? trim(substr($valores, , (strlen($valores) - 2))) : $valores ; // Concatena todas as variáveis e finaliza a instrução $sql .= "INSERT INTO {$this->tabela} (" . $campos . ")VALUES(" . $valores . ")"; // Retorna string com instrução SQL return trim($sql); } /* * Método privado para construção da instrução SQL de UPDATE * @param $arrayDados = Array de dados contendo colunas, operadores e valores * @param $arrayCondicao = Array de dados contendo colunas e valores para condição WHERE * @return String contendo instrução SQL */ private function buildUpdate($arrayDados, $arrayCondicao){ // Inicializa variáveis $sql = ""; $valCampos = ""; $valCondicao = ""; // Loop para montar a instrução com os campos e valores foreach($arrayDados as $chave => $valor): $valCampos .= $chave . '=?, '; endforeach; // Loop para montar a condição WHERE foreach($arrayCondicao as $chave => $valor): $valCondicao .= $chave . '? AND '; endforeach; // Retira vírgula do final da string $valCampos = (substr($valCampos, -2) == ', ') ? trim(substr($valCampos, , (strlen($valCampos) - 2))) : $valCampos ; // Retira vírgula do final da string $valCondicao = (substr($valCondicao, -4) == 'AND ') ? trim(substr($valCondicao, , (strlen($valCondicao) - 4))) : $valCondicao ; // Concatena todas as variáveis e finaliza a instrução $sql .= "UPDATE {$this->tabela} SET " . $valCampos . " WHERE " . $valCondicao; // Retorna string com instrução SQL return trim($sql); } /* * Método privado para construção da instrução SQL de DELETE * @param $arrayCondicao = Array de dados contendo colunas, operadores e valores para condição WHERE * @return String contendo instrução SQL */ private function buildDelete($arrayCondicao){ // Inicializa variáveis $sql = ""; $valCampos= ""; // Loop para montar a instrução com os campos e valores foreach($arrayCondicao as $chave => $valor): $valCampos .= $chave . '? AND '; endforeach; // Retira a palavra AND do final da string $valCampos = (substr($valCampos, -4) == 'AND ') ? trim(substr($valCampos, , (strlen($valCampos) - 4))) : $valCampos ; // Concatena todas as variáveis e finaliza a instrução $sql .= "DELETE FROM {$this->tabela} WHERE " . $valCampos; // Retorna string com instrução SQL return trim($sql); } /* * Método público para inserir os dados na tabela * @param $arrayDados = Array de dados contendo colunas e valores * @return Retorna resultado booleano da instrução SQL */ public function insert($arrayDados){ try { // Atribui a instrução SQL construida no método $sql = $this->buildInsert($arrayDados); // Passa a instrução para o PDO $stm = $this->pdo->prepare($sql); // Loop para passar os dados como parâmetro $cont = 1; foreach ($arrayDados as $valor): $stm->bindValue($cont, $valor); $cont++; endforeach; // Executa a instrução SQL e captura o retorno $retorno = $stm->execute(); return $retorno; } catch (PDOException $e) { echo "Erro: " . $e->getMessage(); } } /* * Método público para atualizar os dados na tabela * @param $arrayDados = Array de dados contendo colunas e valores * @param $arrayCondicao = Array de dados contendo colunas e valores para condição WHERE - Exemplo array('$id='=>1) * @return Retorna resultado booleano da instrução SQL */ public function update($arrayDados, $arrayCondicao){ try { // Atribui a instrução SQL construida no método $sql = $this->buildUpdate($arrayDados, $arrayCondicao); // Passa a instrução para o PDO $stm = $this->pdo->prepare($sql); // Loop para passar os dados como parâmetro $cont = 1; foreach ($arrayDados as $valor): $stm->bindValue($cont, $valor); $cont++; endforeach; // Loop para passar os dados como parâmetro cláusula WHERE foreach ($arrayCondicao as $valor): $stm->bindValue($cont, $valor); $cont++; endforeach; // Executa a instrução SQL e captura o retorno $retorno = $stm->execute(); return $retorno; } catch (PDOException $e) { echo "Erro: " . $e->getMessage(); } } /* * Método público para excluir os dados na tabela * @param $arrayCondicao = Array de dados contendo colunas e valores para condição WHERE - Exemplo array('$id='=>1) * @return Retorna resultado booleano da instrução SQL */ public function delete($arrayCondicao){ try { // Atribui a instrução SQL construida no método $sql = $this->buildDelete($arrayCondicao); // Passa a instrução para o PDO $stm = $this->pdo->prepare($sql); // Loop para passar os dados como parâmetro cláusula WHERE $cont = 1; foreach ($arrayCondicao as $valor): $stm->bindValue($cont, $valor); $cont++; endforeach; // Executa a instrução SQL e captura o retorno $retorno = $stm->execute(); return $retorno; } catch (PDOException $e) { echo "Erro: " . $e->getMessage(); } } /* * Método genérico para executar instruções de consulta independente do nome da tabela passada no _construct * @param $sql = Instrução SQL inteira contendo, nome das tabelas envolvidas, JOINS, WHERE, ORDER BY, GROUP BY e LIMIT * @param $arrayParam = Array contendo somente os parâmetros necessários para clásusla WHERE * @param $fetchAll = Valor booleano com valor default TRUE indicando que serão retornadas várias linhas, FALSE retorna apenas a primeira linha * @return Retorna array de dados da consulta em forma de objetos */ public function getSQLGeneric($sql, $arrayParams=null, $fetchAll=TRUE){ try { // Passa a instrução para o PDO $stm = $this->pdo->prepare($sql); // Verifica se existem condições para carregar os parâmetros if (!empty($arrayParams)): // Loop para passar os dados como parâmetro cláusula WHERE $cont = 1; foreach ($arrayParams as $valor): $stm->bindValue($cont, $valor); $cont++; endforeach; endif; // Executa a instrução SQL $stm->execute(); // Verifica se é necessário retornar várias linhas if($fetchAll): $dados = $stm->fetchAll(PDO::FETCH_OBJ); else: $dados = $stm->fetch(PDO::FETCH_OBJ); endif; return $dados; } catch (PDOException $e) { echo "Erro: " . $e->getMessage(); } } } |
Vamos as explicações dessa classe para CRUD genérico!!
1 – Atributos:
1 2 3 |
private $pdo = null; private $tabela = null; private static $crud = null; |
1 |
private function __construct($conexao, $tabela){} |
3 – Método getInstance():
1 |
public static function getInstance($conexao, $tabela){} |
4 – Método para construção da instrução SQL de INSERT:
1 |
private function buildInsert($arrayDados){} |
1 |
private function buildUpdate($arrayDados, $arrayCondicao){} |
6 – Método para construção da instrução SQL de DELETE:
1 |
private function buildDelete($arrayCondicao){} |
1 |
public function insert($arrayDados){} |
1 |
public function update($arrayDados, $arrayCondicao){} |
9- Método para excluir dados
1 |
public function delete($arrayCondicao){} |
10- Método para consultar dados
1 |
public function getSQLGeneric($sql, $arrayParams=null, $fetchAll=TRUE){} |
Depois das explicações vou demonstrar como usar essa classe chamando os métodos desse CRUD genérico, como exemplo de conexão vou usar uma tabela de usuários ‘TAB_USUARIO’ e a classe de conexão que já foi explicada nesse link:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 |
<?php require_once "Conexao.class.php"; require_once "Crud.class.php"; // Consumindo métodos do CRUD genérico // Atribui uma conexão PDO $pdo = Conexao::getInstance(); // Atribui uma instância da classe Crud, passando como parâmetro a conexão PDO e o nome da tabela $crud = Crud::getInstance($pdo, 'TAB_USUARIO'); // Inseri os dados do usuário $arrayUser = array('nome' => 'João', 'email' => 'joao@gmail.com', 'senha' => base64_encode('123456'), 'privilegio' => 'A'); $retorno = $crud->insert($arrayUser); // Editar os dados do usuario com id 1 $arrayUser = array('nome' => 'João da Silva', 'email' => 'joao@gmail.com.br', 'senha' => base64_encode('654321'), 'privilegio' => 'A'); $arrayCond = array('id=' => 1); $retorno = $crud->update($arrayUser, $arrayCond); // Exclui o registro do usuário com id 1 $arrayCond = array('id=' => 1); $retorno = $crud->delete($arrayCond); // Consulta os dados do usuário com id 1 e privilegio A $sql = "SELECT nome, email, privilegio FROM TAB_USUARIO WHERE id = ? AND privilegio = ?"; $arrayParam = array(1, 'A'); $dados = $crud->getSQLGeneric($sql, $arrayParam, FALSE); |
Bom pessoal nesse artigo demonstrei um CRUD genérico, que pode ser utilizada com vários tipos de banco de dados. Utilizo essa classe diariamente em meus sistemas PHP conectados ao MySQL 5.5, SQL Server 2008 R2 e PostgreSQL 9.1 então posso afirmar com toda certeza que ela supre a grande maioria das necessidades de um sistema de cadastro.
Importante salientar também que esse CRUD genérico não resolve todos os problemas na parte de lógica de negócios envolvendo cadastros, existem cenários mais complexos onde essa classe pode não se adaptar como deveria, exigindo instruções mais específicas.
Para o leitor que tiver disponibilidade em testar essa classe com outros SGBDs, deixo aberto a seção de comentários para postar suas experiências e sugestões de melhorias.