Como usar PDO com banco de dados MySQL

Como usar PDO com banco de dados MySQL

A partir do PHP 5.5, a biblioteca MySQL, do PHP, com funções como mysql_connect e mysql_query é considerada obsoleta. Falei sobre isso neste post. A alternativa mais recomendada é usar a extensão PDO.

Porém muitos programadores ainda não sabem como usar PDO. Vou mostrar neste post como é simples usar essa classe.

É muito importante usar PDO, por duas razões em especial:

  1. Seu código fica mais limpo e moderno
  2. É fácil migrar para outro SGBD, caso seja necessário futurament

Mas, se você leu este meu post, já deve saber disso.

O que é PDO

PDO significa PHP Data Objects (Objetos de Dados do PHP, em tradução livre). Ela é uma classe para gerenciar conexões com bancos de dados. Sim, bancos de dados. Ou seja, é possível usar PDO para conectar com diversos SGBDs, dentre eles MySQL, PostreSQL, SQLite, MS SQL Server, Oracle e outros.

PDO é Orientado a Objetos

PDO é uma classe. Ela segue o padrão da Orientação a Objetos. Caso você não conheça absoluutamente nada sobre Orientação a Objetos, recomendo estudar um pouco sobre isso antes de usar PDO.

Não precisa ir muito a fundo. Apenas entenda o que são classes, objetos e métodos. Veja como instanciar objetos e chamar seus métodos. Isso já é suficiente para trabalhar com PDO.

Recomendo ler estes links:

Tabela usada para este tutorial

Criei uma tabela chamada “programadores”, com ID, nome e site de cada um deles. Utilizei a seguinte SQL para gerar a tabela e seus dados

CREATE TABLE programadores(
    id INT UNSIGNED NOT NULL AUTO_INCREMENT,
    nome VARCHAR(80) NOT NULL,
    site VARCHAR(120) DEFAULT NULL,
    PRIMARY KEY(id)
);
 
INSERT INTO programadores(nome, site) VALUES
('Beraldo', 'http://rberaldo.com.br'),
('João', NULL),
('Maria', 'http://mariaprogramadora.com.br'),
('José', 'http://joseprogramador.com.br'),
('Linus Torvalds', 'http://kernel.org'),
('Mark Zuckerberg', 'http://facebook.com'),
('Steve Wozniak', 'http://apple.com');

Conexão com MySQL usando PDO

Vamos definir constantes para armazenar os dados de conexão.

define( 'MYSQL_HOST', 'localhost' );
define( 'MYSQL_USER', 'root' );
define( 'MYSQL_PASSWORD', '' );
define( 'MYSQL_DB_NAME', 'pdo_tutorial' );

 

A conexão é feita pelo construtor da classe PDO. Ou seja, basta passarmos os dados de conexão como parâmetro, ao instanciarmos a classe PDO, desta forma:

$PDO = new PDO( 'mysql:host=' . MYSQL_HOST . ';dbname=' . MYSQL_DB_NAME, MYSQL_USER, MYSQL_PASSWORD );

Isso funciona. Mas, caso a conexão falhe, será disparada uma exceção e a execução será interrompida.

Para evitarmos isso, devemos colcoar esse trecho em um bloco try...catch e tratar a exceção PDOException. O código ficará assim:

try
{
    $PDO = new PDO( 'mysql:host=' . MYSQL_HOST . ';dbname=' . MYSQL_DB_NAME, MYSQL_USER, MYSQL_PASSWORD );
}
catch ( PDOException $e )
{
    echo 'Erro ao conectar com o MySQL: ' . $e->getMessage();
}

 Executando SELECT com PDO

Tendo a instância da classe PDO na variável $PDO, podemos realizar um simples SELECT desta forma:

$sql = "SELECT * FROM programadores";
$result = $PDO->query( $sql );
$rows = $result->fetchAll();
 
print_r( $rows );

O método query da classe PDO executa uma consulta e retorna, no caso do SELECT, um objeto do tipo PDOStatement. Esse objeto possui o método fetchAll, que é semelhante à função mysql_fetch_array, retornando um array com todos os registros. Usamos a função print_r para exibir esse array. A saída é a seguinte:

Array
(
    [0] => Array
        (
            [id] => 1
            [0] => 1
            [nome] => Beraldo
            [1] => Beraldo
            [site] => http://rberaldo.com.br
            [2] => http://rberaldo.com.br
        )
 
    [1] => Array
        (
            [id] => 2
            [0] => 2
            [nome] => João
            [1] => João
            [site] => 
            [2] => 
        )
 
    [2] => Array
        (
            [id] => 3
            [0] => 3
            [nome] => Maria
            [1] => Maria
            [site] => http://mariaprogramadora.com.br
            [2] => http://mariaprogramadora.com.br
        )
 
    [3] => Array
        (
            [id] => 4
            [0] => 4
            [nome] => José
            [1] => José
            [site] => http://joseprogramador.com.br
            [2] => http://joseprogramador.com.br
        )
 
    [4] => Array
        (
            [id] => 5
            [0] => 5
            [nome] => Linus Torvalds
            [1] => Linus Torvalds
            [site] => http://kernel.org
            [2] => http://kernel.org
        )
 
    [5] => Array
        (
            [id] => 6
            [0] => 6
            [nome] => Mark Zuckerberg
            [1] => Mark Zuckerberg
            [site] => http://facebook.com
            [2] => http://facebook.com
        )
 
    [6] => Array
        (
            [id] => 7
            [0] => 7
            [nome] => Steve Wozniak
            [1] => Steve Wozniak
            [site] => http://apple.com
            [2] => http://apple.com
        )
 
)

Note que, para cada registro, há valores repetidos. Eles aparecem em dois índices: um é o número do campo na tabela e o outro é o nom do campo. Isso ocupa o dobro da memória. Por isso é recomendado retornar apenas o registro com o nome do campo.

Para isso, basta passarmos o parâmetro PDO::FETCH_ASSOC para o método fetchAll. Dessa forma, esse método funcionará de forma semelhante à função mysql_fetch_assoc retornando apenas o array associativo com os nomes dos campos.

Vamos alterar apenas a linha que chama o método fetchAll. Ela ficará assim:

$rows = $result->fetchAll( PDO::FETCH_ASSOC );

O array exibido será este:

Array
(
    [0] => Array
        (
            [id] => 1
            [nome] => Beraldo
            [site] => http://rberaldo.com.br
        )
 
    [1] => Array
        (
            [id] => 2
            [nome] => João
            [site] => 
        )
 
    [2] => Array
        (
            [id] => 3
            [nome] => Maria
            [site] => http://mariaprogramadora.com.br
        )
 
    [3] => Array
        (
            [id] => 4
            [nome] => José
            [site] => http://joseprogramador.com.br
        )
 
    [4] => Array
        (
            [id] => 5
            [nome] => Linus Torvalds
            [site] => http://kernel.org
        )
 
    [5] => Array
        (
            [id] => 6
            [nome] => Mark Zuckerberg
            [site] => http://facebook.com
        )
 
    [6] => Array
        (
            [id] => 7
            [nome] => Steve Wozniak
            [site] => http://apple.com
        )
 
)

Filtrando consultas

Geralmente, nossos SELECTs possuem a cláusula WHERE, para filtrar os resultados.

Isso é muito fáicl de fazer usando PDO.

Vamos considerar a situação seguinte: vamos solicitar, por meio de um formulário, que o usuário defina uma string para usar no filtro do SELECT. A filtragem será feita pelo campo site da nossa tabela.

$search = $_GET['search'];
$sql = "SELECT * FROM programadores WHERE site LIKE '%" . $search . "%'";
$result = $PDO->query( $sql );
$rows = $result->fetchAll( PDO::FETCH_ASSOC );

Vamos supor que o usuário digitou a palavra “programador”. Logo, $_GET['search'] será a string “programador”.

O array $rows será este:

Array
(
    [0] => Array
        (
            [id] => 3
            [nome] => Maria
            [site] => http://mariaprogramadora.com.br
        )
 
    [1] => Array
        (
            [id] => 4
            [nome] => José
            [site] => http://joseprogramador.com.br
        )
 
)

Mas existe uma forma mais eficiente e segura de passar parâmetros para as consultas: usando Prepared Statements.

O que são Prepared Statements e como usá-los

Prepared Statements são úteis para executar uma mesma consulta diversas vezes, com parâmetros distintos, de forma eficiente.

Porém também há outra utilidade: filtragem nativa de consultas, a fim de evitar SQL Injection. Isso dá mais segurança ao seu sistema.

Nesse tipo de consulta, os parâmetros não são enviados diretamente na consulta. Eles são enviados em um “pacote” separado ao servidor MySQL. O servidor, por sua vez, é quem faz a associação entre string SQL e parâmetros.

Em outras palavras, não vamos colocar a variável $search diretamente na consulta.

Nosso código, usando Prepared Statements, ficará assim:

$search = $_GET['search'];
$search = '%' . $search . '%';
$sql = "SELECT * FROM programadores WHERE site LIKE :search";
$stmt = $PDO->prepare( $sql );
$stmt->bindParam( ':search', $search );
$result = $stmt->execute();

Veja que nossa query agora possui apenas o valor “:search” na cláusula WHERE. É assim que a consulta chegará para o MySQL. Com o método bindParam nós dizemos que queremos enviar o valor de $search, que será substituído em :search.

Como estamos usando o LIKE, tivemos que adicionar os sinais de percentual (%), na segunda linha, manualmente. Se estivéssemos buscando por strings exatas (com o operador =), não precisaríamos disso.

O array $row será o mesmo do código anterior.

Usando Prepared Statements, seu código estará muito mais seguro.

E é possível definir o tipo do dado, passando o terceiro parâmetro de bindParam. Os valores possíveis são:

  • PDO::PARAM_BOOL
  • PDO::PARAM_NULL
  • PDO::PARAM_INT
  • PDO::PARAM_STR (esse é o valor padrão)
  • PDO::PARAM_LOB

Esses parâmetros dizem para o MySQL como cada valor deve ser tratado.

Por exemplo, strings devem receber aspas simples ao seu redor. Por outro lado, inteiros não. Por isso é importante passar esses parâmetros, quando o valor não for string.

INSERT, DELETE e UPDATE usando PDO

Esses três comandos serão tratados de forma semelhante.

Vejamos como fazer um INSERT.

$nome = 'Bill Gates';
$site = 'http://microsoft.com';
$sql = "INSERT INTO programadores(nome, site) VALUES(:nome, :site)";
$stmt = $PDO->prepare( $sql );
$stmt->bindParam( ':nome', $nome );
$stmt->bindParam( ':site', $site );
 
$result = $stmt->execute();
 
if ( ! $result )
{
    var_dump( $stmt->errorInfo() );
    exit;
}
 
echo $stmt->rowCount() . "linhas inseridas";

Vamos considerar que $none e site vêm de um formulário também. Por isso precisamos filtrar esses valores usando Prepared Statements.

Para o UPDATE, vamos considerar que desejamos alterar o valor de site para o nome “Bill Gates”. O código é o seguinte:

$nome = 'Bill Gates';
$site = 'http://ruindows.com.br';
$sql = "UPDATE programadores set site = :site WHERE nome = :nome";
$stmt = $PDO->prepare( $sql );
$stmt->bindParam( ':nome', $nome );
$stmt->bindParam( ':site', $site );
 
$result = $stmt->execute();
 
if ( ! $result )
{
    var_dump( $stmt->errorInfo() );
    exit;
}
 
echo $stmt->rowCount() . "linhas alteradas";

Já o DELETE ficará assim:

$nome = 'Bill Gates';
$sql = "DELETE FROM programadores WHERE nome = :nome";
$stmt = $PDO->prepare( $sql );
$stmt->bindParam( ':nome', $nome );
 
$result = $stmt->execute();
 
if ( ! $result )
{
    var_dump( $stmt->errorInfo() );
    exit;
}
 
echo $stmt->rowCount() . "linhas removidas";

 

Conclusão

Espero que tenha achado útil este tutorial. Há muitos outros recursos da extensão PDO. Você pode ler todos eles na Documentação Oficial da Extensão PDO.

  • Mario Junior

    Muito bom!!!

  • Estou meio enferrujado no PHP e preciso lembrar para dar aulas rsrs. Muito legal essas novas funções do PHP 7.

    • Olá.
      O PDO existe, na verdade, desde o PHP 5.1.
      O PHP 7 removeu a extensão mysql, recomendando usar mysqli ou PDO (ambos já existiam há anos).
      Se quiser conhecer as novidades do PHP 7, veja este meu curso gratuito: http://cursophp7.ultimatephp.com.br

  • Antonio Junior

    Obrigado Beraldo por compartilhar essa informação.

    • Olá, Antonio.

      Que bom que o artigo foi útil pra você :)

      Abraço

  • Comecei a estudar PHP há poucas semanas, daí nas pesquisas que fiz eu vi que o PDO é o mais recomendado e estou começando a estudá-lo. Muito obrigado por compartilhar esse conhecimento.

  • Bryan Didur

    Obrigado pelo Ótimo artigo! Ajudou muito!

    Mas eu tenho uma dúvida, vi em alguns lugares que pode-se usar o Prepared Statements usando a ‘?’ na $sql, mas passando o valor da ‘?’ no método execute() da PDO dentro de um array.

    Exemplo:

    $sql = “SELECT * FROM tabela WHERE id = ? “;

    e depois na execução ficaria assim:

    $query->execute( array(1) ); // Funciona normalmente

    Qual é a diferença dessa forma de informar o valor, para as formas que passam valores pelos ” bind’s ” ? Além da não verificação do tipo de dado.

    Desde já agradeço pelos excelentes artigos que posta aqui. Estão me ajudando muito em meus estudos sobre o PHP OO.

    Muito Obrigado Novamente!

    • Eu não gosto muito dessa forma, principalmente quando há muitos parâmetros. Aí fica assim:

      ->prepare(‘SELECT … WHERE para1 = ?, param2 = ?, param3 =? …’)
      ->execute(‘param1’, ‘param2’, ‘param3’…);

      Acho que fica pouco legível. Mas pra quando há poucos parâmetros, não tem problema.