Usamos cookies para medir audiência e melhorar sua experiência. Você pode aceitar ou recusar a qualquer momento. Veja sobre o iMasters.
Buenas pessoal, tudo belezinha? :joia:
Seguinte, comecei a desenvolver uma classe para usar em futuros trabalhos, como é a primeira vez que trabalho com Classe e PDO gostaria da opinião de vocês como posso melhorar o meu código e também verificar os erros do mesmo.
Nessa classe tem uma função que grava logs após usar as funções: Insert, Update, Delete.
Alguém disposto a me ajudar??? :clover:
Segue as classes:
Classe PDO (Connect.inc.php)
<?php
class BD{
private $ConnHost;
private $ConnUser;
private $ConnPass;
private $ConnDb;
private $ConnDriver;
function __construct(){
$this->ConnDriver = "mysql";
$this->ConnHost = "localhost";
$this->ConnUser = "root";
$this->ConnPass = "root";
$this->ConnDb = "testes";
}
function Connect(){
$conn = NULL;
try{
$conn = new PDO($this->ConnDriver.":host=".$this->ConnHost.";dbname=".$this->ConnDb,$this->ConnUser,$this->ConnPass);
}catch (PDOException $e){
$e->getMessage();
echo "Atenção! Ocorreu um erro ao tentar se conectar ao banco de dados.<br />Caso o erro persistir contate o administrador do sistema!";
exit();
}
return $conn;
}
}
?>
Classe CRUD (CRUD.class.php)
<?php
//Classe para conexao BD
include_once("Connect.inc.php");
/***
*
*
* @category Class for Create, Read, Update and Delete (CRUD)
* @author Marcelo Garbin <marcelo.garbin@hotmail.com>
* @copyright 2013 - Marcelo Garbin
* @version 1.0
*
*
***/
class CRUD{
//CRUD
private $SQL;
private $LogSQL;
private $Table;
private $TFields;
private $TValues;
private $Query;
//Set table log
private $TLogs;
function __construct(){
$this->SQL = NULL;
$this->LogSQL = NULL;
$this->Table = NULL;
$this->TFields = NULL;
$this->TValues = NULL;
$this->Query = NULL;
//Set table log
$this->TLogs = "logs_system";
}
function Insert(){
try{
//Dados
$table = self::GetTable();
$campos = self::GetTFields();
$valores = self::GetTValues();
$fields = NULL;
$values = NULL;
$FieldValue = array_combine($campos,$valores);
//Separa campos
foreach($FieldValue as $field => $value){
$fields .= $field.', ';
$values .= $value.', ';
}
//Remove o espaço e virgula final
$fields = substr($fields, 0, strlen($fields)-2);
$values = substr($values, 0, strlen($values)-2);
if(!empty($table) && !empty($fields)){
if(!empty($table) && !empty($fields) && !empty($valores)){
//Cria objeto e abre conexão
$pdo = new BD;
$pdo = $pdo->Connect();
//Monta SQL para insert
self::SetSQL("INSERT INTO ".$table." (".str_replace(':','', $fields).")"." VALUES "."(".$fields.");");
//Prepara Insert
$stmt = $pdo->prepare(self::GetSQL());
//Trata entrada de dados
foreach($FieldValue as $field => $value){
$stmt->bindValue($field, $value);
}
//Executa o metodo
if($stmt->execute() && $stmt->rowCount() > 0){
//Monta SQL para Log
self::SetLogSQL("INSERT INTO ".$table." (".str_replace(':','', $fields).")"." VALUES "."(".$values.");");
//Seta o log e sua acao
self::SetLogs('INSERT');
return true;
}else{
return false;
}
}else{
echo '<br /><strong>Erro: </strong>Faltam complementos para executar a ação!<br />';
}
}
}
catch(PDOException $e){
echo $e->getMessage();
}
$pdo = NULL;
$table = NULL;
$campos = NULL;
$valores = NULL;
$fields = NULL;
$values = NULL;
}
function Update($id){
try{
//Dados
$table = self::GetTable();
$campos = self::GetTFields();
$valores = self::GetTValues();
$fields = NULL;
$values = NULL;
$FieldValue = array_combine($campos,$valores);
//Separa campos
foreach($FieldValue as $field => $value){
$field = str_replace(':','', $field);
$fields .= $field.'=:'.$field.', ';
$values .= $field.'=\''.$value.'\', ';
}
//Remove o espaço e virgula final
$fields = substr($fields, 0, strlen($fields)-2);
$values = substr($values, 0, strlen($values)-2);
if(!empty($table) && !empty($fields)){
if(!empty($table) && !empty($fields) && !empty($valores)){
//Cria objeto e abre conexão
$pdo = new BD;
$pdo = $pdo->Connect();
//Verifica se id existe no BD
$stmt = $pdo->prepare("SELECT * FROM ".$table." WHERE id=:id");
$stmt->bindValue(":id", $id, PDO::PARAM_INT);
$stmt->execute();
if($stmt->rowCount() > 0){
//Monta SQL para insert
self::SetSQL("UPDATE ".$table." SET ".$fields." WHERE id=:id;");
//Prepara Insert
$stmt = $pdo->prepare(self::GetSQL());
//Trata entrada de dados
foreach($FieldValue as $field => $value){
$stmt->bindValue($field, $value);
}
$stmt->bindValue(':id', $id, PDO::PARAM_INT);
//Executa o metodo
if($stmt->execute() && $stmt->rowCount() > 0){
//Monta SQL para Log
self::SetLogSQL("UPDATE ".$table." SET ".$values." WHERE id=".$id.";");
//Seta o log e sua acao
self::SetLogs('UPDATE');
return true;
}else{
return false;
}
}else{
echo '<br /><strong>Error: </strong>Record not found in our database!<br />';
}
}else{
echo '<br /><strong>Error: </strong>Insufficient ons to perform the action!<br />';
}
}
}
catch(PDOException $e){
echo $e->getMessage();
}
$pdo = NULL;
$table = NULL;
$campos = NULL;
$valores = NULL;
$fields = NULL;
$values = NULL;
}
function Delete($id){
try{
//Dados
$table = self::GetTable();
if(!empty($id)){
//Cria objeto e abre conexão
$pdo = new BD;
$pdo = $pdo->Connect();
//Verifica se id existe no BD
$stmt = $pdo->prepare("SELECT * FROM ".$table." WHERE id=:id");
$stmt->bindValue(":id", $id, PDO::PARAM_INT);
$stmt->execute();
if($stmt->rowCount() > 0){
//Monta SQL para insert
self::SetSQL("DELETE FROM ".$table." WHERE id=:id;");
//Prepara Insert
$stmt = $pdo->prepare(self::GetSQL());
//Trata entrada de dados
$stmt->bindValue(':id', $id);
//Executa o metodo
if($stmt->execute() && $stmt->rowCount() > 0){
//Monta SQL para Log
self::SetLogSQL("DELETE FROM ".$table." WHERE id=".$id.";");
//Seta o log e sua acao
self::SetLogs('DELETE');
return true;
}else{
return false;
}
}else{
echo '<br /><strong>Erro: </strong>Registro não encontrado no banco de dados!<br />';
}
}else{
echo '<br /><strong>Erro: </strong>Faltam complementos para executar a ação!<br />';
}
}
catch(PDOException $e){
echo $e->getMessage();
}
$pdo = NULL;
$table = NULL;
$id = NULL;
}
function Select($query){
try{
//Dados
self::SetQuery($query);
if(!empty($query)){
//Cria objeto e abre conexão
$pdo = new BD;
$pdo = $pdo->Connect();
//Verifica se query e valida
if($stmt = $pdo->query(self::GetQuery())){
//Retorna todos os valores ref. a query
$res = $stmt->fetchAll(PDO::FETCH_ASSOC);
return $res;
}else{
echo '<br /><p><strong>Error: </strong>Invalid Query, check the SQL code!<br /></p>';
return false;
}
}
}catch(Exception $e){
echo $e->getMessage();
}
$pdo = NULL;
}
function SetLogs($acao){
try{
//Cria objeto e abre conexão
$pdo = new BD;
$pdo = $pdo->Connect();
//Prepata insert
$stmt = $pdo->prepare("INSERT INTO ".$this->TLogs."(ip_remote, login, password, action, action_sql, date_time) VALUES (:ip_remote, :login, :password, :action, :action_sql, NOW())");
//Trata entrada de dados
$stmt->bindValue(':ip_remote', $_SERVER['REMOTE_ADDR']);
$stmt->bindValue(':login', $login=null);
$stmt->bindValue(':password', $senha=null);
$stmt->bindValue(':action', $acao);
$stmt->bindValue(':action_sql', self::GetLogSQL());
//Executa o metodo
$stmt->execute();
}
catch(PDOException $e){
echo $e->getMessage();
}
$pdo = NULL;
}
/* ////// */
/* Set's */
/* //// */
function SetTable($table = NULL){
$this->Table = $table;
}
function SetTFields($campos = NULL){
$this->TFields = $campos;
}
function SetTValues($valores = NULL){
$this->TValues = filter_var_array(array_map("htmlspecialchars",$valores), FILTER_SANITIZE_STRING);
}
function SetSQL($sql = NULL){
$this->SQL = $sql;
}
function SetLogSQL($sql = NULL){
$this->LogSQL = $sql;
}
function SetQuery($query = NULL){
$this->Query = $query;
}
/* ////// */
/* Get's */
/* //// */
function GetTable(){
return $this->Table;
}
function GetTFields(){
return $this->TFields;
}
function GetTValues(){
return $this->TValues;
}
function GetSQL(){
return $this->SQL;
}
function GetLogSQL(){
return $this->LogSQL;
}
function GetQuery(){
return $this->Query;
}
}
?>SELECT sem prepare ??
Eu tenho uma dúvida sobre criar um select passando os valores via array e tratar com o bindParam...
Exemplo:
Classe::Read('Database', 'Tabela', 'WHERE', 'OFFSET, 'LIMIT', 'ORDERBY');
Queria ter uma ideia de como pegar o WHERE até o ORDERBY e separar cada valor no bindParam do PDO até por que o WHERE pode ter mais de uma condição assim como o ORDERBY.
Se pensarmos em algo assim e criarmos creio que seria útil o SELECT fora isso não vejo motivo de usar o PDO.
Se pensarmos em algo assim e criarmos creio que seria útil o SELECT fora isso não vejo motivo de usar o PDO.
A principal utilidade do PDO é quanto à abstração do SGDB que vai ser utilizado. Utilizando PDO você troca de MySQL pra Postgres, pra SQLite, Firebird, MSSQL só mudando o construtor e adaptando as consultas.
A principal utilidade do PDO é quanto à abstração do SGDB que vai ser utilizado. Utilizando PDO você troca de MySQL pra Postgres, pra SQLite, Firebird, MSSQL só mudando o construtor e adaptando as consultas.
Entendo... Mas e no sentido de tratar as querys antes de enviar para a base de dados? O Read sem bindParam não teria utilidade, seria enviados injections tranquilamente...
Você não precisa ficar refém do bind. Tem uma outra forma que eu acho bem mais sexy ;)
$stmt = $pdo->prepare('select titulo, conteudo, capa from posts where datapost = :data and author = :autor');
$stmt->execute($_POST);
var_dump($stmt->fetchAll());Mas e em relação ao crud? E se quiser fazer ele dinâmico como eu disse acima
Classe::Read('Database', 'Tabela', 'WHERE', 'OFFSET, 'LIMIT', 'ORDERBY');
Uma sugestão de ORM bem leve e simples é o redbeanPHP, um include e ele esta pronto para o uso - http://www.redbeanphp.com
>
Mas e em relação ao crud? E se quiser fazer ele dinâmico como eu disse acima
Classe::Read('Database', 'Tabela', 'WHERE', 'OFFSET, 'LIMIT', 'ORDERBY');
Cara, classe crud só não é um equívoco maior do que estender um Usuário com uma classe DB. A propósito, seguindo a linha de desenvolvimento deste tópico, seria - provavelmente - o próximo passo.
O tipo de flexibilidade qu você almeja pode ser alcançado implementando o padrão interpreter
@Enrico, suas dicas são muito boas, porém como sou novato com classes pra mim tudo é um obstacúlo :no: (Sim, pretendo passar por esses obstacúlos, estou pesquisando material e códigos pela web.
Teria algum exemplo da classe pdo recebendo por parametro a configuração? Gostei muito dessa ideia e até então nem havia me tocado pelo fato de eu estar focado apenas com mysql, foi muito valida sua colocação.
Deletei toda a classe BD e deixei apenas o __construct, isso aqui:
<?php
class DB{
private $DBDriver;
private $DBHost;
private $DBUser;
private $DBPass;
private $DBName;
function __construct($DBDriver, $DBHost, $DBUser, $DBName, $DBPass, $Exception){
$this->$conn = new PDO($this->DBDriver.":host=".$this->DBHost.";dbname=".$this->DBName,$this->DBUser,$this->DBPass);
}
}<?php
require_once("./includes/Connect.inc.php");
//
$obj = new DB('mysql','localhost','root','root','testes',true);
var_dump($obj);
Bom isso é apenas um teste(mal sucedido) para tentar entender como fazer essa tal conexão hehehe..
Dicas ai?
Crud é abstrato demais para ser abstraído e não significa banco de dados. Acho que esse é o maior equívoco do código acima.
Não adianta querermos uma solução única, rápida para fazer cruds rápidos, ela não vai existir.
Entendo... Mas e no sentido de tratar as querys antes de enviar para a base de dados? O Read sem bindParam não teria utilidade, seria enviados injections tranquilamente...
CRUD não significa banco de dados, como mencionado. Uma implementação:
Nossa entidade usuário:
<?php
namespace BeautifulUserCrud;
use RuntimeException;
/**
* Exception thrown when some validation fails.
*/
class ValidationError extends RuntimeException
{
}
/**
* Representation of an user.
*/
class User
{
private $name;
private $email;
private $password;
/**
* Creates a user.
* @param string $name
* @param string $email
* @param string $password
* @param string $repeatedPassword The password the user typed again for security reasons.
* @throws ValidationError If the name is shorter than 2 characters.
* @throws ValidationError If the email isn't valid.
* @throws ValidationError If the password is shorter than 6 characters.
* @throws ValidationError If the repeated password isn't equal to the first password.
* @todo Use a validation library.
*/
public function __construct($name, $email, $password, $repeatedPassword)
{
if (strlen($name) < 2) {
throw new ValidationError('O nome deve conter no mínimo 2 caracteres.');
}
if (filter_var($email, FILTER_VALIDATE_EMAIL) === false) {
throw new ValidationError('O email informado não é válido.');
}
if (strlen($password) < 6) {
throw new ValidationError('A senha deve conter no mínimo 6 caracteres.');
}
if ($password !== $repeatedPassword) {
throw new ValidationError('As senhas informadas não são iguais.');
}
$this->name = $name;
$this->email = $email;
$this->password = $password;
}
/**
* Returns the user name.
* @return string
*/
public function getName()
{
return $this->name;
}
/**
* Returns the user email.
* @return string
*/
public function getEmail()
{
return $this->email;
}
/**
* Returns the user password.
* @return string
*/
public function getPassword()
{
return $this->password;
}
}
Nossa interface que serve como contrato para um storage (banco de dados, por exemplo):
<?php
namespace BeautifulUserCrud;
use RuntimeException;
/**
* Exception thrown when an user doesn't exist.
*/
class UserNotFound extends RuntimeException
{
}
/**
* Contract for storages that will be able to store users.
*/
interface UserDataStorage
{
/**
* Inserts an user.
* @param User $user
*/
public function insert(User $user);
/**
* Updates an existing user.
* @param int $id
* @param User $user
* @throws UserNotFound If there's no user with such id.
*/
public function update($id, User $user);
/**
* Deletes an user.
* @param int $id
* @throws UserNotFound If there's no user with such id.
*/
public function delete($id);
/**
* Find all the users.
* @return User[]
*/
public function find();
/**
* Find an user by his id.
* @param int $id
* @throws UserNotFound If there's no user with such id.
*/
public function findOneById($id);
}
A implementação do PDO. É fictícia pelo tempo :(:
<?php
namespace BeautifulUserCrud;
use RuntimeException;
/**
* Handle user storage using PDO.
*/
class PDOUserDataStorage implements UserDataStorage
{
private $pdo;
public function __construct(PDO $pdo)
{
$this->pdo = $pdo;
}
/**
* {@inheritDoc}
*/
public function insert(User $user)
{
}
/**
* {@inheritDoc}
*/
public function update($id, User $user)
{
}
/**
* {@inheritDoc}
*/
public function delete($id)
{
}
/**
* {@inheritDoc}
*/
public function find()
{
}
/**
* {@inheritDoc}
*/
public function findOneById($id)
{
}
}
E por último poderíamos ter um controller recebendo um storage no construtor e fazendo todo o trabalho. Acho que deu para entender um pouco de abstração, responsabilidade e para que os comentários realmente servem.
- O método está enorme e com uma grande complexidade ciclomática, reduza isso.
O PHP.MD reclama DEMAIS sobre isso mas não ajuda em nada na hora de solucionar. Quando eu pegava no pesado com os códigos, certa vez eu rodei sobre uma rotina de adição de cache e ele reclamou porque aparentemente tinha muitos IFs.
Mas o código do método em si estava enxutíssimo, fazia apenas o que deveria ser feito. Esse é um dos problemas mais tretas de resolver.
- Esse try, como comentado anteriormente, é desnecessário.
Pelo que pude observar, não é desnecessário não. Como ele está usando a PDO e a PDOStatement também dispara PDOExceptions, deve sim utilizar. O erro aí está que o try está muito longe do catch, ele deveria vir imediatamente antes de invocar o PDO::prepare().
- Não crie uma conexão para cada método executado, isso destrói a performance.
- Como o método Connect da classe BD retorna, você acaba por violar a Lei de Demeter se usada dessa forma. Um ponto a ser considerado.
Não sei quem é esse fulano, mas é fácil de resolver esses dois pontos.
No método de conexão, ao invés de armazenar o objeto numa variável e retorná-la, você armazena numa propriedade (previamente criada, claro) e, no início do método você verifica se há uma conexão (no caso da PDO basta usar instanceof). Se houver um objeto, retorna a propriedade, se não houver você faz a conexão, popula a dita propriedade e a retorna.
Dado o fluxo vertical e crescente, se alguém lá atrás conectou ao banco a propriedade vai estar com o objeto e reinvocar o método de conexão, não vai sobrecarregar o sistema.
No método de conexão, ao invés de armazenar o objeto numa variável e retorná-la, você armazena numa propriedade (previamente criada, claro) e, no início do método você verifica se há uma conexão (no caso da PDO basta usar instanceof). Se houver um objeto, retorna a propriedade, se não houver você faz a conexão, popula a dita propriedade e a retorna.
O Registry pode ajudar nisso, não?!
Sim e não. O Registry vai permitir a você perpetuar o objeto, a grosso modo, de uma classe pra outra. O caso aí é que isso evita que sejam abertas várias conexões.;
>
O PHP.MD reclama DEMAIS sobre isso mas não ajuda em nada na hora de solucionar. Quando eu pegava no pesado com os códigos, certa vez eu rodei sobre uma rotina de adição de cache e ele reclamou porque aparentemente tinha muitos IFs.
Mas o código do método em si estava enxutíssimo, fazia apenas o que deveria ser feito. Esse é um dos problemas mais tretas de resolver.
Eu nem falo do PHP MD, mas de bater o olho dá para ver que a complexidade ciclomática é alta. Para cache, Decorator é um bom pattern que substitui os if's.
Não é que seja fácil de resolver, mas há vários if's e complexidades desnecessárias.
Pelo que pude observar, não é desnecessário não. Como ele está usando a PDO e a PDOStatement também dispara PDOExceptions, deve sim utilizar. O erro aí está que o try está muito longe do catch, ele deveria vir imediatamente antes de invocar o PDO::prepare().
Se fosse para ignorar a exception ou lançar uma exception mais específica, aí tudo bem. Mas pegar uma exception para dar um echo + exit....
>
Não sei quem é esse fulano, mas é fácil de resolver esses dois pontos.
No método de conexão, ao invés de armazenar o objeto numa variável e retorná-la, você armazena numa propriedade (previamente criada, claro) e, no início do método você verifica se há uma conexão (no caso da PDO basta usar instanceof). Se houver um objeto, retorna a propriedade, se não houver você faz a conexão, popula a dita propriedade e a retorna.
Dado o fluxo vertical e crescente, se alguém lá atrás conectou ao banco a propriedade vai estar com o objeto e reinvocar o método de conexão, não vai sobrecarregar o sistema.
http://en.wikipedia.org/wiki/Law_of_Demeter - É só um nome de homenagem, não sei para quem exatamente. O problema é que, como você disse, o método retorna o objeto e o método é chamado diretamente, gerando acoplamento (é uma "lei" no sentido de que isso sempre acontece, apesar do nome parecer arrogante).
Ao invés disso:
<?php
class BD{
private $ConnHost;
private $ConnUser;
private $ConnPass;
private $ConnDb;
private $ConnDriver;
function __construct(){
$this->ConnDriver = "mysql";
$this->ConnHost = "localhost";
$this->ConnUser = "root";
$this->ConnPass = "root";
$this->ConnDb = "testes";
}
function Connect(){
$conn = new PDO($this->ConnDriver.":host=".$this->ConnHost.";dbname=".$this->ConnDb,$this->ConnUser,$this->ConnPass);
return $conn;
}
}
class CRUD{
function Insert(){
$pdo = new DB;
$pdo = $pdo->Connect();
$pdo->prepare('..');
}
}
Basta usar a PDO e injetar a dependência.
<?php
class CRUD{
private $pdo;
function __construct(PDO $pdo){
$this->pdo = $pdo;
}
function Insert(){
$this->pdo->prepare('..');
}
}
$pdo = new PDO('mysql:host=localhost;dbname=testes', 'root', 'root');
$crud = new CRUD($pdo);Agora, eliminamos o problema de criar conexão toda hora, o problema do acoplamento, o problema da Lei de Demeter e eliminamos uma classe desnecessária.
Entendi agora, você faz/configura a conexão pelo construct da classe que você vai utilizar os métodos, assim faz com que ela seja chamada apenas uma vez para todos os métodos, isso mesmo?
Logo a classe ou arquivo de configuração não tem necessidade de existir, apenas o objeto PDO que é passsado como referência em um novo objeto, certo?
Por aí.
A classe CRUD depende de um objeto do tipo PDO, declarado aqui:
>
function __construct(PDO $pdo){
E podemos simplesmente passar uma instância da PDO ao criar uma classe.
Dependency Injection. É um conceito simples, mas fantástico. Se todos que trabalham com OO conhecessem não veríamos tanta porcaria de singleton, registry, static e afins por aí.
Bom Todos comentaram e deram suas dicas, também criei uma classe CRUD quando estava começando a estudar PDO, veja, se puder tire exemplos, agradeço...
#Config.php
Padrão de projeto: Registry
class Config {
public static $data = array();
public static function write($key, $value) {
self::$data[$key] = $value;
}
public static function read($value) {
if (!isset(self::$data[$value])) {
return 'Nao existe ' . self::$data[$value];
}
return self::$data[$value];
}
public static function checkReg($name) {
if (array_key_exists($name, self::$data)) {
return true;
} else {
exit('<p>Registro ( ' . $name . ' ) nao Existe...</p>' . __FILE__);
}
}
}
#database.php
Aplicado o Padrão de projeto: Registry
/**
* -----------------------------------------------------------------------
* Constante do ambiente de desenvolvimento, onde pode-se trabalhar com multiplos
* banco de dados.
* -----------------------------------------------------------------------
*/
define('ENVIRONMENT','desenvolvimento');
/**
* -----------------------------------------------------------------------
* DADOS DE CONEXAO COM BANCO DE DADOS -> PDO
* -----------------------------------------------------------------------
* Aqui vocé deve definir suas configurações de banco de dados de acordo
* com um determinado ambiente de desenvolvimento. Vocé pode definir quantos
* ambientes forem necessários.
* -----------------------------------------------------------------------
*/
Config::write('database', array(
'desenvolvimento' => array(
'host' => 'localhost',
'dbname' => 'tabela-db',
'user' => 'root',
'pass' => '',
'dbdriver' => 'mysql',
'charset' => 'utf8',
'options' => null
),
'teste' => array(
'host' => 'localhost',
'dbname' => 'mysql',
'user' => 'root',
'pass' => '',
'dbdriver' => 'mysql',
'charset' => 'utf8',
'options' => null
),
'producao' => array(
'host' => 'localhost3',
'dbname' => 'mysql3',
'user' => 'root3',
'pass' => '',
'dbdriver' => 'mysql',
'charset' => 'utf8',
'options' => null
)
));
#Model.php
Aplicando padrão de projeto, singleton, registry, active record, table data gateway.
<?php
class Model {
protected $db;
private static $instance;
public $_tabela;
public function __construct() {
$this->db = self::getInstance();
}
public static function getInstance() {
if (is_null(self::$instance)) {
if(Config::checkReg('database')):
$config = Config::read('database');
endif;
$host = $config[ENVIRONMENT]['host'];
$dbname = $config[ENVIRONMENT]['dbname'];
$dbdriver = $config[ENVIRONMENT]['dbdriver'];
$username = $config[ENVIRONMENT]['user'];
$passwd = $config[ENVIRONMENT]['pass'];
$options = $config[ENVIRONMENT]['options'];
$charset = $config[ENVIRONMENT]['charset'];
$dsn = $dbdriver . ':host=' . $host . ';dbname=' . $dbname . '';
self::$instance = new PDO($dsn, $username, $passwd, $options);
return self::$instance;
}
return self::$instance;
}
/**
* Metodo de Inserir no banco de dados
* @param $dados um array indice=>valor com os dados
* @return boolean retorna booleano do insert
*/
public function insert(Array $dados) {
$campos = implode(',', array_keys($dados));
$valores = "'" . implode("','", array_values($dados)) . "'";
$query = $this->db->query("INSERT INTO {$this->_tabela} ({$campos}) VALUES ({$valores})");
return $query;
}
/**
* Metodo para leitura de banco de dados
* @param string $where $where clausula de where sql
* @param number $limit limite da consulta
* @param number $offset empura pra frente o limit
* @param string $orderby ordenação da consulta
* @return Array Array retorna um array associativo
*/
public function read($where = null, $limit = null, $offset = null, $orderby = null) {
$where = ($where != null ? "WHERE {$where}" : "");
$limit = ($limit != null ? "LIMIT {$limit}" : "");
$offset = ($offset != null ? "OFFSET {$offset}" : "");
$orderby = ($orderby != null ? "ORDER BY {$orderby}" : "");
$q = $this->db->query("SELECT * FROM {$this->_tabela} {$where} {$orderby} {$limit} {$offset} ");
return $q->fetchAll(PDO::FETCH_ASSOC);
}
/**
* Metodo para atualização de dados no Database
* @param $dados um array indice=>valor com os dados
* @param $where param que vai gerenciar
* @return boolean retorna um true ou false
*/
public function update(Array $dados, $where = null) {
foreach ($dados as $ind => $val) {
$campos[] = "{$ind} = '{$val}'";
}
$campos = implode(", ", $campos);
$where = ($where != null ? "WHERE {$where}" : "");
$query = $this->db->query("UPDATE {$this->_tabela} SET {$campos} {$where} ");
return $query;
}
/**
* Metodo que deleta uma linha no banco
* @param $where param que vai gerenciar ex: id=1
* @return boolean retorna um true ou false
*/
public function delete($where = null) {
$where = ($where != null ? "WHERE {$where}" : "");
$query = $this->db->query("DELETE FROM {$this->_tabela} {$where} LIMIT 1");
return $query;
}
}
#PostModel.php
Trabalhando com a classe, extendendo Model.php onde vai constar todo o CRUD da aplicação!
Já utilizando ActiveRecord e Table Data Gateway que são padrões de projeto interessantes para master uma abstração.
<?php
class PostModel extends Model {
public $_tabela = 'posts';
public function inserir_post($dados = array()){
$query = $this->insert($dados);
return $query;
}
public function listar_post($limit = null){
$query = $this->read();
return $query;
}
public function atualiza_post($dados = array() , $where){
$query = $this->update($dados, $where);
return $query;
}
public function deletar_post($where){
$query = $this->delete($where);
return $query;
}
}
#Finalizado
Parei de trabalhar no projeto, mas ainda falta muita coisa para ficar 100% bom, por exemplo:
Não usei Abstract Factory, Interfaces, quis fazer algo simples, limpo e didatico.
Abraços, e trabalhe duro para que seu código seja 100% organizado!
@Thadeu, globais cara, não use...
Model singleton??? Hmmm, não é uma boa ideia. Aliás, o que você chama de model não é exatamente um Model, é apenas uma camada de acesso a banco de dados, que está contida no Model.
Aqui vai mais um exemplo, utilizando MySQLi invés de PDO:
http://henriquebarcelos.in/blog/2012/08/20/php-oo-classe-simples-para-acesso-a-banco-de-dados/
Ótima contribuição Henrique, obrigado!
>
@Thadeu, globais cara, não use...
Model singleton??? Hmmm, não é uma boa ideia. Aliás, o que você chama de model não é exatamente um Model, é apenas uma camada de acesso a banco de dados, que está contida no Model.
Aqui vai mais um exemplo, utilizando MySQLi invés de PDO:
http://henriquebarcelos.in/blog/2012/08/20/php-oo-classe-simples-para-acesso-a-banco-de-dados/
@Henrique, Qual o motivo de não se utilizar globais? Tire como exemplo o framework CodeIgniter que segundo pesquisas indicam que esteja entre os 3 melhores frameworks de PHP da atualidade, e que UTILIZA GLOBAIS.
No meu caso eu utilizo uma global para verificar qual o tipo de ambiente que estou desenvolvendo(se não percebeu), que por sinal é muito parecido com o CodeIgniter(CI). Isso possibilita que eu possa trabalhar com até 3 bancos de dados diferentes,so modificando um array.
Verifiquei sua classe http://henriquebarce...banco-de-dados/ e encontrei muitos erros e muitos detalhes que estão ultrapassados para desenvolvedores atualizados, não preciso entrar em detalhes porque seria anti-ético.
@Henrique Devemos evitar o máximo possível escrever códigos SQL, por isso devemos utilizar ActiveRecord, MVC,TableDataGateway e outros padrões que muitas pessoas acham que não são importantes, mas que fazem grande diferença no runtime de uma aplicação.
Conheço seu blog e sei que você é um ótimo desenvolvedor :D por isso, todo conselho é bem-vindo e anotei sua sugestão e vou estudar e trabalhar para melhorar ainda mais meu codigo.
Obrigado mesmo cara de coração.
Abraços!!!!
>
@Henrique, Qual o motivo de não se utilizar globais? Tire como exemplo o framework CodeIgniter que segundo pesquisas indicam que esteja entre os 3 melhores frameworks de PHP da atualidade, e que UTILIZA GLOBAIS.
Mais utilizado não significa, necessariamente, melhor.
Segundo pesquisas encomendadas por quem? Pela EllisLab?
>
No meu caso eu utilizo uma global para verificar qual o tipo de ambiente que estou desenvolvendo(se não percebeu), que por sinal é muito parecido com o CodeIgniter(CI). Isso possibilita que eu possa trabalhar com até 3 bancos de dados diferentes,so modificando um array.
Para isso, você pode utilizar constantes ou até mesmo um arquivo de configurações externo.
Verifiquei sua classe http://henriquebarce...banco-de-dados/ e encontrei muitos erros e muitos detalhes que estão ultrapassados para desenvolvedores atualizados, não preciso entrar em detalhes porque seria anti-ético.
O fórum existe exatamente para debater e crescermos juntos. Não há anti ética em questionar os motivos de qualquer abordagem que seja. Ajuda a entender quando podemos quebrar as regras ;)
Ou foi anti ético ele questionar sua decisão sobre globais?
Edit: quanto às técnicas ultrapassadas, acho que você verificou a data de postagem do artigo?
Gurizada medonha!
Eu to começando.. relaxam aí! rsrsrsrs.....
To me "matando" pra sair da casinha do tal procedural e partir pro POO...
Minhas dificuldade inicial, com a classe que postei no primeiro post deste tópico, esta fazendo esse serviço aqui => CRUD
Porém entre as ajudais ai preciso mudar muita coisa nela, uma coisa que gostaria de mudar é a conexão, gostaria de fazer algo parecido com a que você fez @Henrique, utilizando PDO.
Como posso fazer?
Exemplo: Tenho 2 Classes, 1 onde tem o CRUD e outra onde tem a conexão com o PDO.
Como faço para passar os parametros e criar o objeto do PDO no construct do CRUD? Isso que não consegui ainda...
Estou tentando, daqui a pouco tenho aula, mas vou continuar vendo isso até conseguir rsrsrs...
Qual o motivo de não se utilizar globais? Tire como exemplo o framework CodeIgniter que segundo pesquisas indicam que esteja entre os 3 melhores frameworks de PHP da atualidade, e que UTILIZA GLOBAIS.
O motivo é óbvio: comportamento inesperado, dificuldade para testar, acoplamento. Uma simples pesquisa no Google poderia abrir os horizontes.
Eu não quero entrar em motivo de framework x ou y, mas o CodeIgniter é um dos frameworks mais mal feitos. Ele é popular porque é fácil de um iniciante qualquer usar, não porque ele é bom. Hoje em dia, Symfony2 e ZF2 são os frameworks mais modernos/usados e eles evitam estado global.
No meu caso eu utilizo uma global para verificar qual o tipo de ambiente que estou desenvolvendo(se não percebeu), que por sinal é muito parecido com o CodeIgniter(CI). Isso possibilita que eu possa trabalhar com até 3 bancos de dados diferentes,so modificando um array.
Dê uma olhada no Dependency Injection Container do Symfony2. Ele te fornece um mecanismo bem completo de configuração, muito superior do que colocar arrays globais :sick:.
Verifiquei sua classe http://henriquebarce...banco-de-dados/ e encontrei muitos erros e muitos detalhes que estão ultrapassados para desenvolvedores atualizados, não preciso entrar em detalhes porque seria anti-ético.
Anti-ético é falar mal duma pessoa, não dum código. O código não representa a pessoa.
Devemos evitar o máximo possível escrever códigos SQL, por isso devemos utilizar ActiveRecord, MVC,TableDataGateway e outros padrões que muitas pessoas acham que não são importantes, mas que fazem grande diferença no runtime de uma aplicação.
Em sistemas muito complexos, ORMs podem não escalar. Em projetos pequenos, podem ser muito complexos e pesados para resolver um simples problema. Há uma discussão eterna nisso, não é uma regra.
Como faço para passar os parametros e criar o objeto do PDO no construct do CRUD? Isso que não consegui ainda...
Qual a dificuldade? o PDO deveria ser passado para o construct, a classe CRUD não deveria criar o PDO. Poste o código que você está tentando fazer ;).
Qual a dificuldade? o PDO deveria ser passado para o construct, a classe CRUD não deveria criar o PDO. Poste o código que você está tentando fazer ;).
É que eu nunca usei classes, e o material que estou usando de estudo pego da internet.. então tem muita coisa que é antiga ou não é a melhor forma de fazer, quero aprender desde o começo o que é certo...
To fazendo uns testes aqui, teria como ver se isso esta certo?
Ou não é assim?
Connect.inc.php
<?php
class BD extends PDO{
private $conn;
function __construct($conn = null){
$this->conn = $conn;
$this->Connect($this->conn);
}
function Connect($conn = null){
try{
$this->conn = parent::__construct($this->conn['driver'].':host='.$this->conn['host'].';dbname='.$this->conn['dbname'], $this->conn['user'], $this->conn['pass']);
$this->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
return $this->conn;
}catch(PDOException $e){
echo '<br /><strong>Erro ao se conectar no servidor!</strong><br /><strong>Linha:</strong> '.$e->getLine().' / <strong>Código:</strong> '.$e->getCode().'<br />';
}
}
function __destruct(){
$this->conn = null;
}
}<?php
require_once("Connect.inc.php");
class CRUD extends BD{
private $sql;
private $pdo;
function __construct($pdo = null){
$this->pdo = $pdo;
$this->pdo = new BD($this->pdo);
}
function select(){
foreach ($this->pdo->query("SELECT * FROM tb_names") as $row) {
print $row['name'] . $row['surname'] . "<br>";
}
}
}
index.php
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Teste PDO</title>
</head>
<body>
<?php
require_once("crud.class.php");
$obj = new CRUD(array("driver"=>"mysql","host"=>"localhost","dbname"=>"testes","user"=>"root","pass"=>""));
$obj->select();
?>
</body>
</html>Vish... vamos lá :lol:.
É que eu nunca usei classes, e o material que estou usando de estudo pego da internet.. então tem muita coisa que é antiga ou não é a melhor forma de fazer, quero aprender desde o começo o que é certo...
De fato, você tem que tomar cuidado. Eu sugiro que você pesquise material sobre OOP em Java, pois o conteúdo é bem mais elaborado geralmente, em PHP há muita "porcaria".
O primeiro passo é pensar em objetos, não em classes.
>
class BD extends PDO{
private $conn;
function __construct($conn = null){
$this->conn = $conn;
$this->Connect($this->conn);
}
function Connect($conn = null){
try{
$this->conn = parent::__construct($this->conn['driver'].':host='.$this->conn['host'].';dbname='.$this->conn['dbname'], $this->conn['user'], $this->conn['pass']);
$this->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
return $this->conn;
}catch(PDOException $e){
echo '<br /><strong>Erro ao se conectar no servidor!</strong><br /><strong>Linha:</strong> '.$e->getLine().' / <strong>Código:</strong> '.$e->getCode().'<br />';
}
}
function __destruct(){
$this->conn = null;
}
}
>
class CRUD extends BD{
Um dos erros mais comuns. Herança tem um significado: é um.
Quando nós estendemos uma classe, semanticamente declaramos que a classe é um tipo da que está sendo estendida. Por exemplo: se dizemos que a classe SQLite vai estender Database, eu estou dizendo que SQLite é um Database, que nesse caso faz sentido.
Sempre que for estender, pergunte: essa classe é um tipo da outra? se for, herança pode ser adequada.
CRUD é um BD? Não => não use herança
SQLite é um BD? Sim => pode usar herança
>
private $sql;
Por que essa propriedade foi definida?
>
function __construct($pdo = null){
$this->pdo = $pdo;
$this->pdo = new BD($this->pdo);
Não dá para entender. Por que você permite null, define uma propriedade uma vez e depois a redefine? WTF?
Let's go!
>
Configuração não deve ser posta diretamente numa classe. Há dois principais problemas: menor flexibilidade e você torna difícil uma coisa tão banal, nesse caso: conexões múltiplas. Além disso, considere ver o Open/Closed Principle.
>
>
?>
Como o arquivo inteiro é somente PHP, a tag de fechamento não deve ser utilizada.
>
include_once("Connect.inc.php");
Autoloading, namespacing e PSR-0.
>
O valor padrão das propriedades é null. Você não precisa gastar processamento e tamanho de arquivo para dizer uma "redundância redundante de uma forma redundante".
>
$this->TLogs = "logs_system";
O mesmo problema do construtor da outra classe. Além disso, não é responsabilidade dessa classe fazer logs. Uma solução boa seria usar eventos/hooks/signals, usando um Observer ou Mediator.
>
Bem, isso vale tanto para o Insert quanto para os outros que são bem parecidos.
Por último, isso:
>
//Classe para conexao BD
/***
*
*
//Set table log
//Dados
//Separa campos
//Remove o espaço e virgula final
//Cria objeto e abre conexão
//Monta SQL para insert
//Prepara Insert
//Trata entrada de dados
//Executa o metodo
//Monta SQL para Log
//Seta o log e sua acao
/ ////// /
/ Set's /
/ //// /
/ ////// /
/ Get's /
/ //// /
Você realmente precisa desses comentários? Por quê?
Comentários redundantes poluem o código. Comentários com o propósito de explicar melhor o código acabam poluindo e ainda mascara um código ruim.
Os comentários são realmente úteis para documentar a API pública e te ajudar a lembrar de tarefas através de @todo's e @fixme's.