Nesta aula, vamos explorar como realizar consultas ao banco de dados utilizando a extensão PDO (PHP Data Objects) do PHP. PDO é uma interface de acesso a banco de dados que fornece uma camada de abstração, permitindo trabalhar com diferentes bancos de dados de forma consistente. Aprenderemos os métodos básicos para executar consultas, como usar prepared statements para evitar injeção SQL, como vincular parâmetros e como recuperar dados usando diferentes modos de fetch.

Antes de prosseguir, certifique-se de ter uma conexão PDO estabelecida com seu banco de dados. Nos exemplos, usaremos um banco de dados MySQL com uma tabela chamada 'usuarios' com colunas 'id', 'nome', 'email' e 'idade'.

query e exec

O método query() é usado para executar consultas SQL que não possuem parâmetros dinâmicos, como SELECT simples. Ele retorna um objeto PDOStatement que pode ser usado para iterar sobre os resultados. Já o método exec() é usado para consultas que não retornam resultados, como INSERT, UPDATE ou DELETE, e retorna o número de linhas afetadas.

É importante notar que query() não escapa os valores; portanto, nunca deve ser usado com dados fornecidos pelo usuário. Use prepared statements para isso. Exemplos:

<?php
// Usando query() para SELECT
$stmt = $pdo->query('SELECT * FROM usuarios');
while ($row = $stmt->fetch()) {
    print_r($row);
}

// Usando exec() para INSERT
$count = $pdo->exec("INSERT INTO usuarios (nome, email, idade) VALUES ('João', 'joao@example.com', 30)");
echo "Linhas inseridas: $count";
?>

Prepared statements

Prepared statements são a forma recomendada de executar consultas com parâmetros dinâmicos no PDO. Eles previnem injeção SQL ao separar a estrutura da consulta dos dados. O processo envolve preparar a consulta com placeholders (marcadores de posição) e depois executá-la com os valores reais.

Existem dois tipos de placeholders: posicionais (?) e nomeados (:nome). Os placeholders posicionais são indexados por posição, enquanto os nomeados são associados a nomes. Exemplo com placeholders nomeados:

<?php
$sql = "SELECT * FROM usuarios WHERE idade > :idade AND email LIKE :email";
$stmt = $pdo->prepare($sql);
$stmt->execute([':idade' => 25, ':email' => '%@example.com']);
$resultados = $stmt->fetchAll();
print_r($resultados);
?>

Bind de parâmetros

O bind de parâmetros é uma alternativa ao array passado diretamente no execute(). Ele permite associar variáveis a placeholders, definindo o tipo de dado esperado. Os métodos bindParam() e bindValue() são usados para isso. A diferença é que bindParam() vincula a variável por referência, permitindo que seu valor seja alterado antes da execução, enquanto bindValue() vincula o valor atual da variável.

Exemplo com bindParam:

<?php
$stmt = $pdo->prepare("INSERT INTO usuarios (nome, email, idade) VALUES (:nome, :email, :idade)");
$nome = 'Maria';
$email = 'maria@example.com';
$idade = 28;
$stmt->bindParam(':nome', $nome);
$stmt->bindParam(':email', $email);
$stmt->bindParam(':idade', $idade, PDO::PARAM_INT);
$stmt->execute();

// Alterando valores e executando novamente
$nome = 'Pedro';
$email = 'pedro@example.com';
$idade = 35;
$stmt->execute();
?>

Fetch modes

Os fetch modes determinam como os dados são retornados ao recuperar linhas do banco. PDO oferece vários modos, sendo os mais comuns:

  • PDO::FETCH_ASSOC: retorna um array associativo com nomes das colunas como chaves.
  • PDO::FETCH_NUM: retorna um array indexado numericamente.
  • PDO::FETCH_BOTH: retorna ambos (padrão).
  • PDO::FETCH_OBJ: retorna um objeto anônimo com propriedades correspondentes às colunas.
  • PDO::FETCH_CLASS: retorna uma instância de uma classe específica.

Exemplo de uso:

<?php
$stmt = $pdo->query('SELECT * FROM usuarios');

// FETCH_ASSOC
$assoc = $stmt->fetchAll(PDO::FETCH_ASSOC);
print_r($assoc);

// FETCH_OBJ
$obj = $stmt->fetchAll(PDO::FETCH_OBJ);
foreach ($obj as $usuario) {
    echo $usuario->nome . "\n";
}
?>

Boas práticas e observações finais

Sempre prefira prepared statements para qualquer consulta que envolva dados do usuário, mesmo que sejam apenas números. Isso garante segurança contra injeção SQL. Utilize query() apenas para consultas estáticas. Lembre-se de tratar exceções com try-catch, pois PDO lança exceções se configurado para isso (modo de erro ERRMODE_EXCEPTION).

Outra dica: ao usar bindParam(), defina explicitamente o tipo do parâmetro usando constantes como PDO::PARAM_INT para inteiros, PDO::PARAM_STR para strings, etc. Isso otimiza a consulta e evita conversões desnecessárias.

Referências

Exercícios

  1. Crie uma conexão PDO com um banco de dados MySQL local e execute uma consulta SELECT usando query() para listar todos os registros de uma tabela 'produtos'.
  2. ✓ Resposta:
    <?php
    $pdo = new PDO('mysql:host=localhost;dbname=teste', 'usuario', 'senha');
    $stmt = $pdo->query('SELECT * FROM produtos');
    while ($row = $stmt->fetch(PDO::FETCH_ASSOC)) {
        print_r($row);
    }
    ?>
  3. Usando prepared statements com placeholders nomeados, insira um novo usuário na tabela 'usuarios'. Os campos são: nome, email e idade.
  4. ✓ Resposta:
    <?php
    $sql = "INSERT INTO usuarios (nome, email, idade) VALUES (:nome, :email, :idade)";
    $stmt = $pdo->prepare($sql);
    $stmt->execute([
        ':nome' => 'Ana',
        ':email' => 'ana@example.com',
        ':idade' => 22
    ]);
    echo "Usuário inserido com sucesso.";
    ?>
  5. Utilize bindParam() para atualizar o email de um usuário cujo id é fornecido por uma variável. O email deve ser alterado para 'novo@example.com'.
  6. ✓ Resposta:
    <?php
    $id = 1;
    $email = 'novo@example.com';
    $sql = "UPDATE usuarios SET email = :email WHERE id = :id";
    $stmt = $pdo->prepare($sql);
    $stmt->bindParam(':email', $email, PDO::PARAM_STR);
    $stmt->bindParam(':id', $id, PDO::PARAM_INT);
    $stmt->execute();
    echo "Email atualizado.";
    ?>
  7. Faça uma consulta SELECT que retorne todos os usuários com idade maior que 18 e exiba os resultados como objetos (FETCH_OBJ).
  8. ✓ Resposta:
    <?php
    $stmt = $pdo->prepare("SELECT * FROM usuarios WHERE idade > :idade");
    $stmt->execute([':idade' => 18]);
    $usuarios = $stmt->fetchAll(PDO::FETCH_OBJ);
    foreach ($usuarios as $user) {
        echo $user->nome . " - " . $user->email . "\n";
    }
    ?>
  9. Crie uma consulta usando exec() para deletar todos os usuários com idade inferior a 18. Mostre quantas linhas foram deletadas.
  10. ✓ Resposta:
    <?php
    $count = $pdo->exec("DELETE FROM usuarios WHERE idade < 18");
    echo "Foram deletados $count usuários.";
    ?>