Ir para conteúdo

POWERED BY:

Arquivado

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

Maykel-ctba

Conversão de MySQL comum para API PDO

Recommended Posts

Fala galera,

 

Vendo vários tópicos, muita gente está migrando o seu formato de consulta para a API PDO ao invés da consulta normal e comum MySQL.

 

Vendo essa necessidade de segurança, eu quero atualizar um sistema que tenho. Esse sistema utiliza um framework que eu mesmo criei em conjunto com um amigo meu que trabalhou comigo muito tempo atrás, e eu toquei o projeto e o utilizo diariamente.

 

Neste framework, tenho um módulo para consultar (SELECT), e aonde ele faz a montagem dessa consulta, hoje utilizo mysql_query.

 

Poderiam me ajudar? :innocent:

 

Trecho de consulta:

try 
{
    $conexao = initConnection();
    $this->sql = "SELECT $campo FROM ".$tabela." ".$where." ".$group." ".$order." ".$limit ." ".$having;

    if (!$resultado = mysql_query($this->sql))
    {
        $this->erro = "Erro nº.:".mysql_errno().". ".mysql_error();
        throw new Exception();
    }
    else
    {
        $this->total_registros = mysql_num_rows($resultado);
    }
    
    exitConnection($conexao);
    
}
catch (Exception $e)
{
    $this->total_registros = 0;
}

 

Gostaria de migrar para o PDO, mas não sei os comandos similares para mysql_num_rows, e nem como fazer a conexão, que já está aberta em initConnection().

 

 

 

Outras funções:

function initConnection()
{
    $hostname_sistema = "localhost";
    $database_sistema = "projeto";
    $username_sistema = "root";
    $password_sistema = "";
    
    $conexao = mysql_connect($hostname_sistema, $username_sistema, $password_sistema) or trigger_error(mysql_error(),E_USER_ERROR);

    if($conexao)
    {
        mysql_select_db($database_sistema, $conexao);
        mysql_query("SET NAMES 'utf8'");
        mysql_query('SET character_set_connection = utf8');
        mysql_query('SET character_set_client = utf8');
        mysql_query('SET character_set_results = utf8');
    }
    
    return $conexao;
}

function exitConnection($conexao){
    mysql_close($conexao);
}

Compartilhar este post


Link para o post
Compartilhar em outros sites

Dá uma lida na documentação e pesquise por exemplos no google.

 

http://php.net/pdo

Compartilhar este post


Link para o post
Compartilhar em outros sites

@William, valeu pela dica!

 

Pesquisando, cheguei a um resultado, mas devido a aplicação, não consigo testar no momento.

Só gostaria de saber se a lógica abaixo está certa, para que eu possa avançar ao segundo passo.

 

Adaptei o arquivo de conexão para ficar assim... erro na tela, não deu :innocent:

 

function initConnection()
{
    $hostname_sistema = "localhost";
    $database_sistema = "projeto";
    $username_sistema = "root";
    $password_sistema = "";
    
    $conexao = new PDO('mysql:host='.$hostname_sistema.';dbname='.$database_sistema.'', $username_sistema, $password_sistema);
    
    if($conexao)
    {
        $conexao->query("SET NAMES 'utf8'");
        $conexao->query("SET character_set_connection = utf8");
        $conexao->query("SET character_set_connection = utf8");
        $conexao->query("SET character_set_results = utf8");
    }
    return $conexao;
}

function exitConnection($conexao)
{
    $conexao->closeCursor();
}

 

Minha dúvida maior é como fechar a conexão. O closeCursor é o recurso correto?

Compartilhar este post


Link para o post
Compartilhar em outros sites

Fui aplicar agora uma consulta básica, e deu erro:

 

$conexao = initConnection(); // Abre a conexão

$this->sql = "SELECT bloId FROM sistema_blog WHERE bloExcluido = 'N'";

if($resultado = $conexao->query($this->sql))
{
    $this->total_registros = $conexao->exec($resultado);
}
else
{
    $this->erro = "Erro: ".$conexao->errorInfo();
    throw new Exception();
}

exitConnection($conexao); // Fecha a conexão

 

Pq diabos dá esse erro?

  • Warning: PDO::exec() expects parameter 1 to be string, object given in /Applications/XAMPP/xamppfiles/htdocs/common/class/drago.class.php on line 995

Sendo que, estou passando o parâmetro $resultado no exec? :/

Compartilhar este post


Link para o post
Compartilhar em outros sites

Manuar pra tu! http://www.php.net/manual/pt_BR/pdo.exec.php

public int PDO::exec(string $statement)

O método exec() pede como parâmetro um SQL Statement e não um PDOStatement..

$pdo->exec('SELECT ...');

 

Aconselha-se a usar Prepared Statements por conta dos parâmetros posicionais/nomeados, veja: http://www.php.net/manual/pt_BR/pdo.prepared-statements.php

Compartilhar este post


Link para o post
Compartilhar em outros sites

@Marcielo resolveu, parça! O erro sumiu, mas não estou conseguindo fechar a conexão agora, haha... os erros vão aparecendo aos poucos :P

 

  • Fatal error: Call to undefined method PDO::closeCursor() in/Applications/XAMPP/xamppfiles/htdocs/common/function/connect.phpon line 68

 

function exitConnection($conexao)
{
	$conexao->closeCursor();
}

 

Esse parametro $conexão está logo acima, primeira linha do post #4 do tópico! A função de conexão (obj PDO) está no post #3.

Compartilhar este post


Link para o post
Compartilhar em outros sites

Vamos lá,

 

 

$conexao = initConnection(); // Abre a conexão

$this->sql = "SELECT bloId FROM sistema_blog WHERE bloExcluido = 'N'";

if($resultado = $conexao->query($this->sql))
{
    $this->total_registros = $conexao->exec($resultado);
}
else
{
    $this->erro = "Erro: ".$conexao->errorInfo();
    throw new Exception();
}

exitConnection($conexao); // Fecha a conexão

Nada de if/else aí, defina para sua conexão lance exceções:

$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);

 

Outro ponto é que, porque raios você já não trabalha somente com OOP? :wacko: É tão mais elegante fazer:

class Connection {

    private static $connection;

    private static function factory() {
        return new PDO('...');
    }
    
    public static function getConnection() {
        if (self::$connection === null) {
            self::$connection = self::factory();
        }
        
        return self::$connection;
    }
    
    public static function close() {
        self::$connection = null;
    }
}

Para fechar a conexão com a base basta destruir a referência..

 

Ah e neste último exemplo ainda se "garante" que você tenha uma única conexão para a aplicação.

Compartilhar este post


Link para o post
Compartilhar em outros sites

Acredito o jeito mais simples seja atribuir null

 

 

function exitConnection($conexao) {
 $conexao = null;
}

 

 

da olhada nesses links

http://www.diogomatheus.com.br/blog/php/trabalhando-com-pdo-no-php/
http://www.rafaelwendel.com/2011/12/tutorial-pdo-php-data-object/

Compartilhar este post


Link para o post
Compartilhar em outros sites

@Marcielo Eu juro que queria entender tudo isso e saber como aplicar ao meu caso, mas acho que hoje não é o meu dia HAHAHA

 

Acho que vou começar por um projeto menor e ver como isso funciona, e depois aplicar ao meu projeto haha, achei que a principio seriam apenas mudar alguns comandos por outros, mas vi que o buraco é mais embaixo, hehe!

Compartilhar este post


Link para o post
Compartilhar em outros sites

Foi apenas uma sugestão..

 

Não sei que ponto você não entendeu, mas de qualquer forma vou explicar.. seria mais benéfico e gerenciável se você tivesse uma classe de conexão como mostrado no post #9.

 

Não é viável ter várias instâncias de conexão à cada consulta que você fizer por exemplo. Então geralmente utilizamos um padrão Singleton pra resolver isso. Isso explica o método getConnection(), que seria a sua função initConnection()

 

 

public static function getConnection() {
    if (self::$connection === null) {
        self::$connection = self::factory();
    }
        
    return self::$connection;
}

O método apenas verifica se há uma conexão aberta, caso ainda não tenha a variável connection estaria nula e então cria uma nova conexão, por fim retorna a conexão.

 

Para que você entenda melhor a parte de "fechar a conexão", entenda que na classe PDO é como se houvesse um método __destruct() que fechasse literalmente a conexão pra você.

class PDO {
    public function __destruct() {
        // fecha a conexão
    }
}

Portanto quando atribuímos um valor nulo pra conexão ela irá perder sua referência de memória e o método PDO::__destruct() será invocado.

 

 

Na parte que eu disse para você definir pra que sua conexão lance exceções é porque por padrão ela não faz isso, se você manda executar um SQL com erro sua conexão simplesmente não fará nada e no caso de uma SELECT retornará um valor vazio.

 

Já quando definido você poderá tratar erros mais facilmente com try/catch por exemplo, veja:

try {
    $stmt = $pdo->prepare('SELECT ...');
    $stmt->execute();
} catch(PDOException $e) {
    echo $e->getMessage();
}

Caso ainda tenha ficado dúvidas é só dizer, e diga qual o ponto você não entendeu ^_^

Compartilhar este post


Link para o post
Compartilhar em outros sites

Na real as dúvidas são várias. Eu estou tão familiarizado com o PHP comum que a Orientação a objetos ainda é coisa recente na minha cabeça. Consegui o que tenho a partir de exemplos que fui adaptando... e mesmo assim não sei se é o certo. Quero fazer um projeto menor, mas do jeito certo, e de um jeito que seja fácil de adaptar caso precise replicar ele.

 

O conceito de OO ainda é vago pra mim.

 

Hoje, o que eu faço?

 

1) Um arquivo de um módulo do meu sistema onde contém todas as funções referentes a ele (ex: usuario.class.php).

2) Este arquivo contem um "class Usuario { }" contendo variáveis privates que utilizo nas funções, os constructs (determinando um valor padrão para cada variável)

3) Crio uma função de atribuição de valores pra cada construct

4) Crio as funções de automação (Cadastro, edição, remoção, listagem, etc...)

 

Exemplo básico Usuario.class.php:

class Usuario 
{
    private $usuId;
    private $usuNome;
    
    function __construct() 
    {
        $this->usuId = 0;
        $this->usuNome = "";
    }
    
    function ID($valor = null)
    {
        if (!is_null($valor)) { $this->usuId = $valor; }
        else { return $this->usuId; }
    }
    
    function Nome($valor = null)
    {
        if (!is_null($valor)) { $this->usuNome = $valor; }
        else { return $this->usuNome; }
    }
    
    // * Automação de cadastros
    function cadastrar()
    {
        $colunas = array();
        $valores = array();
        
        $colunas[] = "usuNome";
        $valores[] = $this->usuNome;
        
        $query = new Query();
        $query->setTabela(array("sistema_usuario"));
        $query->setCampo($colunas);
        $query->setValor($valores);
        $resultado = $query->insert();
        
        if ($resultado)
        {
            return $query->getCodigo();
        }
        else 
        {
            return 0;
        }
    }
}

$objRegistro = new Usuario();
$objRegistro->Nome("Fulano de tal");
$resultadoCadastro = $objRegistro->cadastrar();

if(!empty($resultadoCadastro))
{
    echo "Cadastrado";
}
else
{
    echo "Deu erro";
}

 

A class Query() utilizada acima é uma outra classe que gerencia o banco de dados, um pouco maior (isso que resumi bastante, haha, deixei só as funções de INSERT):

 

require_once($_SERVER['DOCUMENT_ROOT']."/common/function/connect.php");
	 
class Query
{
    protected $tabelas;
    protected $campos;
    protected $valores;
    protected $condicao;
    protected $condicaoManual;
    protected $ordem;
    protected $limite;
    protected $agrupar;
    protected $total_registros;
    private $sql;
    private $erro;
    private $codigo;
    
    function __construct()
    {
        $this->tabelas = null;
        $this->campos = null;
        $this->where = null;
        $this->agrupar = null;
        $this->ordem = null;
        $this->limite = null;
        $this->codigo = null;
        $this->total_registros = 0;
    }

    function setTabela($valor)
    {
        $this->tabelas = $valor;
    }

    function setCampo($valor)
    {
        $this->campos = $valor;
    }
    
    function setValor($valor)
    {
        $this->valores = $valor;
    }
    
    function setCondicao($valor)
    {
        $this->condicao = $valor;
    }
    
    function setCondicaoManual($valor)
    {
        $this->condicaoManual = $valor;
    }

    function setAgrupar($valor)
    {
        $this->agrupar = $valor;
    }

    function setOrdem($valor)
    {
        $this->ordem = $valor;
    }
    
    function setHaving($valor)
    {
        $this->having = $valor;
    }

    function setLimite($valor)
    {
        $this->limite = $valor;
    }

    function setTotal($valor)
    {
        $this->total_registros = $valor;
    }

    function setSQL($valor)
    {
        $this->sql = $valor;
    }

    function setErro($valor)
    {
        $this->erro = $valor;
    }

    function getTabela()
    {
        return $this->tabelas;
    }

    function getCampo()
    {
        return $this->campos;
    }
    
    function getValor()
    {
        return $this->valores;
    }

    function getCondicao()
    {
        return $this->condicao;
    }

    function getAgrupar()
    {
        return $this->agrupar;
    }

    function getOrdem()
    {
        return $this->ordem;
    }

    function getTotal()
    {
        return $this->total_registros;
    }

    function getSQL()
    {
        return $this->sql;
    }

    function getErro()
    {
        return $this->erro;
    }

    function getCodigo()
    {
        return $this->codigo;
    }
    
    function insert()
    {
        $total_tabelas = count($this->tabelas);
        $total_campos = count($this->campos);
        $total_valores = count($this->valores);
        $sem_erro = true;

        if ($total_tabelas > 1)
        {
            $sem_erro = false;
        }

        if ($total_campos > 0)
        {
            for ($i = 0; $i < $total_campos; $i++)
            {
                if (empty($campo))
                {
                    $campo = trim(addslashes($this->campos[$i]));
                }
                else
                {
                    $campo .= ", ".trim(addslashes($this->campos[$i]));
                }
            }
        }
        
        if ($total_valores > 0)
        {
            for ($i = 0; $i < $total_valores; $i++)
            {
                if (empty($valor))
                {
                    $valor = "'".trim(addslashes($this->valores[$i]))."'";
                }
                else
                {
                    $valor .= ", '".trim(addslashes($this->valores[$i]))."'";
                }
            }
        }
        else
        {
            $sem_erro = false;
        }

        if ($sem_erro)
        {
            try
            {
                $conexao = initConnection();

                if (!empty($campo))
                {
                    $nome_campos = "(".$campo.")";	
                }

                $this->sql = "INSERT INTO ".$this->tabelas[0]." ".$nome_campos." VALUES (".$valor.")";

                if (!$resultado = mysql_query($this->sql))
                {
                    $this->erro = "Erro nº.:".mysql_errno().". ".mysql_error();
                    throw new Exception();
                }
                else
                {
                    $this->codigo = mysql_insert_id();
                }

                exitConnection($conexao);
            }
            catch (Exception $e)
            {
                $resultado = 0;
            }

            if ($resultado)
            {
                return $resultado;
            }
            else
            {
                return $resultado;
            }
        }
        else
        {
            return $sem_erro;
        }
    }
}

 

Não sei se esse é o jeito certo, quero melhorar, e sempre reduzir o código dentro do possível, desde que eu consiga manter uma certa flexibilidade entre um projeto e outro. Hoje eu consigo, mas devido as várias vantagens do PHP PDO, quero migrar para esse formato.



Outra duvida é... preciso fazer uma classe para as conexões com o banco? Seria recomendável?

 

E depois, incluir essa classe aonde preciso chamá-la?

Compartilhar este post


Link para o post
Compartilhar em outros sites

Consegui o que tenho a partir de exemplos que fui adaptando... e mesmo assim não sei se é o certo.

Sempre digo: "programação é uma arte, programar sem saber o que está fazendo é caca"

 

Eu estou tão familiarizado com o PHP comum que a Orientação a objetos ainda é coisa recente na minha cabeça.

 

O conceito de OO ainda é vago pra mim.

Não só para você, mas para muitos. O fato é que geralmente instituições por exemplo já começam ensinando da forma "errada", ensinando o sujeito a programar estruturado, sendo que depois ele vai ter que aprender a programar Orientado a Objetos que envolve uma outra forma de pensar. Mas por mais que para alguns a princípio seja mais fácil entender estruturado, terá sérias dificuldades no futuro. Outro ponto é que na maioria das vezes se aprende os conceitos e a codificar OO, mas não se aprende alguns conceitos fundamentais que envolvem OO. Bom, essa é a minha opinião então não vou estender o assunto. OOP não é algo novo e conteúdo na própria internet pra estudar não falta.

 

 

1) Um arquivo de um módulo do meu sistema onde contém todas as funções referentes a ele (ex: usuario.class.php).

Pelo que vi na implementação você possui uma espécie de ActiveRecord, pra falar a verdade sou totalmente contra esse tipo de implementação, pois viola os princípios SRP.

 

 

2)...contendo variáveis privates que utilizo nas funções, os constructs (determinando um valor padrão para cada variável)

Não é necessário inicializar valores com null, por padrão eles já assumem esse valor, demais valores exceto instâncias por exemplo podem ser inicializados no momento da criação dos atributos, por exemplo:

class Foo {
    private $bar; // null
    private $string = '';
    private $int = 0;
    private $double = 10.5;
    private $object; // null

    public function __construct() {
        $this->object = new Object();
    }
}

 

3) Crio uma função de atribuição de valores pra cada construct

 

    function ID($valor = null)
    {
        if (!is_null($valor)) { $this->usuId = $valor; }
        else { return $this->usuId; }
    }
    // ...

Isso deveriam ser setters/getters..

 

 

4) Crio as funções de automação (Cadastro, edição, remoção, listagem, etc...)

Sua classe possui mais de uma responsabilidade, ela além de representar um usuário ainda gerência funcionalidades de CRUD do mesmo (leia sobre SRP - Single Responsibility Principle) essa seria uma implementação para uma outra classe, um DAO (Data Access Object) por exemplo.

 

 

 

require_once($_SERVER['DOCUMENT_ROOT']."/common/function/connect.php");

Um autoloader poderia resolver a inclusão de arquivos..

 

 

Sobre sua classe Query, acho interessante essas classes de manipulação de SQL Statements, elas proporcionam uma interface mais agradável e dão a ilusão de se trabalhar com SQL de forma Orientada a Objetos, porém no fim isso se resume apenas em montar uma string.

 

 

 

        if ($total_campos > 0)
        {
            for ($i = 0; $i < $total_campos; $i++)
            {
                if (empty($campo))
                {
                    $campo = trim(addslashes($this->campos[$i]));
                }
                else
                {
                    $campo .= ", ".trim(addslashes($this->campos[$i]));
                }
            }
        }

Essa parte é totalmente desnecessária, novamente: Prepared Statements, o PDO cuida disso pra você.

 

 

 

$conexao = initConnection();

Sua classe não deve ter conhecimento de como obter uma conexão, ela deve ser injetada em um setter por exemplo ou pelo construtor já que para o funcionamento ela dependente da conexão.

 

 

 

            try
            {
                // ...

                if (!$resultado = mysql_query($this->sql))
                {
                    $this->erro = "Erro nº.:".mysql_errno().". ".mysql_error();
                    throw new Exception();
                }
                else
                {
                    $this->codigo = mysql_insert_id();
                }

                exitConnection($conexao);
            }
            catch (Exception $e)
            {
                $resultado = 0;
            }

Isso me cheira mais a gambiarra.. o sucesso da consulta foi testado, você ainda lança uma exceção para capturar dentro do próprio método, sendo que podia ter logo atribuído o valor 0 ao resultado ao invés de lançar a exceção. Enfim, nem era pra existir tratamentos aí. E como eu disse se você definir para o PDO lançar exceções você não precisará fazer isso.

 

 

 

$this->codigo = mysql_insert_id();

PDO também possui um método parecido lastInsertId():

$pdo->lastInsertId();

Esse poderia ser o retorno do seu método que executasse o insert.

 

 

"Garantir o retorno do valor a qualquer custo.." Não, pera... :huh:

 

if ($resultado)
{
    return $resultado;
}
else
{
    return $resultado;
}

Não sei o que você quis fazer aí.. :cry:

 

 

Outra duvida é... preciso fazer uma classe para as conexões com o banco? Seria recomendável?

Sim para as duas perguntas. Supondo que você tenha que alterar algum parâmetro de conexão por exemplo, imagine só alterar todos os locais onde você usou :o

 

Fazendo uma autocorreção em relação a implementação do exemplo do post #9. É que o singleton não deve ser usado para persistências, há um problema levando em conta que possam haver várias conexões em um mesmo projeto. O padrão Factory é mais adequado.

 

 

E depois, incluir essa classe aonde preciso chamá-la?

Não exatamente.. quando fazemos isso:

class DAO {
    
    private $connection;

    public function __construct() {
         $this->connection = new PDO('...');
    }
}

Ou até mesmo isso..

class DAO {

    public function insert(Foo $foo) {
         $connection = Connection::getConnection();
         // ...
    }
}

Estamos dizendo que o DAO deve conhecer a conexão ou como obtê-la e isso aumenta o acoplamento.

 

Por isso ela deve ser injetável:

class DAO {

    private $connection;

    public function __construct(PDO $connection) {
        $this->connection = $connection;
    }
}

Compartilhar este post


Link para o post
Compartilhar em outros sites

Simplesmente uma aula, @Marcielo! :clap:

 

Ontem comecei a montar do zero um novo projeto, e vi várias dessas inconsistências que você citou. O fato é que cheguei com o projeto meio encaminhado, e depois que autor saiu, apenas dei continuidade e não o revisei desde o começo, mas continuei utilizando.

 

Ontem até me embananei com algumas questões que podem ser vistas aqui: http://forum.imasters.com.br/topic/515614-iniciando-com-php-pdo-e-oo-erro-na-funo-prepare/

 

Mas vou dar uma lida novamente nesse tópico e ver o que posso aplicar daqui, neste novo projeto.

 

 

-----

 

E as dúvidas vão surgindo, haha :innocent:

 

Por exemplo, você comentou do principio ActiveRecord. Li na wiki e é bem isso o que faço... mas o conceito de Single responsibility ficou vago... nesse caso, cada classe teria que ter uma função apenas... isso não encheria de classes sobre um mesmo "tema"? Não seria... digamos, redundante? Já que utilizo os mesmos construtores, atributos, etc...

Compartilhar este post


Link para o post
Compartilhar em outros sites

mas o conceito de Single responsibility ficou vago... nesse caso, cada classe teria que ter uma função apenas... Não seria... digamos, redundante? Já que utilizo os mesmos construtores, atributos, etc...

O que você faria seria separar responsabilidades, onde poderia haver redundância nisso? Cada classe deve possuir uma ÚNICA responsabilidade. No caso do ActiveRecord identifiquei duas responsabilidades que poderiam ser divididas, em uma Entity (entidade que representa os dados do BD) e DAO (responsável pelo acesso ao BD):

class Foo {
    private $bar;

    public function setBar($bar) {
        $this->bar = $bar;
    }

    public function getBar() {
        return $this->bar;
    }
}

class FooDAO {
    public function __construct(PDO $connection) ...
    public function insert(Foo $foo) ...
    public function update(Foo $foo) ...
    public function remove($id) ...
    public function findById($id) ...
    ...
}

 

isso não encheria de classes sobre um mesmo "tema"?

 

Aí vai depender da quantidade de responsabilidades que você tiver. Eu não usaria esse termo "encher de classes". O objetivo é esse, separar responsabilidades, uma responsabilidade pode ter várias funcionalidades talvez seja essa uma dúvida sua, dizer que uma classe deve ter uma única responsabilidade não quer dizer que necessariamente ela deve ter um só método :upset: assim como salvar, editar, excluir... faz parte de uma única responsabilidade que é interagir com o BD e portanto possam ser métodos distintos. Um tópico onde debatem um pouco sobre o assunto de SRP é este: http://forum.imasters.com.br/topic/510435-solid-princpio-da-responsabilidade-nica/, lembrando que o SRP é apenas um subprincípio de um outro conjunto de princípios chamado SOLID que também recomendo que você leia sobre.

Compartilhar este post


Link para o post
Compartilhar em outros sites

×

Informação importante

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