Skip to content

2.4.3 PHP Data Objects(PDO)

Cássio Santos edited this page Apr 21, 2017 · 1 revision

O PDO veio para solucionar a migração de um banco de dados para outro, do MySQL para o PostgreSQL, por exemplo. O PDO é uma camada de abstração de acesso a dados, onde os métodos de manipulação de dados são independentes do SGDB que você está utilizando, ou seja, podemos usar as mesmas funções para executar queries e consultar dados. O PDO veio no PHP 5.1 e dá suporte a vários sistemas gerenciadores de banco de dados, como MySQL, PostgreSQL, SQlite, Informix, Oracle, SQL Server, IBM e etc. A conexão com um banco de dados através do PDO se dá durante a criação de um objeto da classe PDO, passando informações de conexão com o banco na forma de um DSN (Data Source Name), além das credencias de acesso, como mostra o próximo exemplo.

<?php
    // MySQL

    $db= new PDO("mysql:host=localhost;dbname=banco", "root", "senha");

    // PostgreSQL

    $db = new PDO("pgsql:host=localhost;dbname=banco", "root", "senha");

    // SQLite

    $db = new PDO("sqlite:banco.sqlite");
?>

Veja que criamos a $db que guarda um objeto da classe PDO e entre parênteses passamos o host, nome do banco, usuário e senha. Uma vez que o objeto da classe PDO tenha sido instanciado, conectamos em nosso banco. Para desconectar, basta "matarmos" o objeto ou aguardar que ele seja morto automaticamente ao final de nosso script, como podemos ver a seguir:

<?php
    $db = new PDO("mysql:host=localhost;dbname=banco", "root", ""); //É recomendado que a instância do PDO seja passada por parâmetro.
    unset($db);
?>

Veja que utilizamos o unset para encerrar a conexão.

Executando Comandos

Depois de conectados temos a nossa disposição uma série de métodos para lidar com o banco. Utilizando a linguagem SQL, vamos fazer o uso do método exec, de acordo com o código do próximo exemplo.

<?php  
    $db = new PDO("mysql:host=localhost;dbname=banco", "root", ""); //É recomendado que a instância do PDO seja passada por parâmetro.

    $db->exec("CREATE TABLE clientes(id INT AUTO_INCREMENT, nome VARCHAR(255), email VARCHAR(255)) ");
?>

Veja que acessamos o método exec através de nossa conexão com o “->” e criamos uma tabela chamada clientes com os campos id, nome e email.

Fazendo Consultas

Para fazer consultas usamos o método query, que executa um comando SQL e traz para nós linhas de um banco de dados. Veja o próximo exemplo.

<?php
    $db = new PDO("mysql:host=localhost;dbname=banco", "root", ""); //É recomendado que a instância do PDO seja passada por parâmetro.

    $dados = $db->query("SELECT * FROM clientes");
?>

Veja que dentro da $dados é executada uma query que traz todos os dados da tabela clientes. Podemos ainda acessar nossos dados através dos métodos fetch e fetchAll. O método fetch retorna apenas um resultado para nós, enquanto o fetchAll irá retornar todos os resultados. Estes métodos retornam tanto um array quando um objeto, dependendo dos parâmetros especificados, como exemplo a seguir.

<?php
    $db = new PDO("mysql:host=localhost;dbname=banco", "root",""); //É recomendado que a instância do PDO seja passada por parâmetro.
    $dados = $db->query("SELECT * FROM clientes");
    $todos = $dados->fetchAll();
    $um = $dados->fetch();

    print_r($todos);
    print_r($um);
?>

Note que, por padrão, os métodos retornam índices associativos e numéricos. Podemos fazer com que somente índices associativos sejam mostrados ou apenas numéricos, como mostra o código a seguir

<?php
    $db = new PDO("mysql:host=localhost;dbname=banco", "root",""); //É recomendado que a instância do PDO seja passada por parâmetro.
    $dados = $db->query("SELECT * FROM clientes");
    $assoc = $dados->fetchAll(PDO::FETCH_ASSOC);
    $num = $dados->fetchAll(PDO::FETCH_NUM);

    print_r($assoc);
    print_r($num);
?>

Veja que na $assoc queremos que seja retornado resultados em índices associativos, enquanto que na $num retorna apenas índices numéricos.

Transações

Transações são uma sequência de operações feitas em um sistema gerenciador de banco de dados. As transações têm o objetivo de proporcionar uma maneira de recuperar informações a partir de um desastre. Uma transação de banco de dados deve possuir atomicidade, consistência, isolamento e durabilidade (ACID).

Imagine um sistema onde temos que inserir dados em tabelas de estoque, pedido, cliente e logística. O cliente pagou e tudo foi inserido aparentemente bem no sistema, mas não foi inserido o pedido de entrega na tabela de logística. Temos um problema agora, certo?!

As transações evitam esse tipo de problema através do método beginTransaction do PDO. Utilizamos do try-catch, que simplificando seria como: tente, falhando, pegue. No try, após chamarmos o método, os comandos serão feitos para que possam ser executados. O PDO irá esperar pelo método commit para efetivar os comandos no banco de dados. Entretanto pode ocorrer erros e nem todos os comandos serão efetuados, neste caso entramos no catch, no qual será tratado o comando rollback para anular os comandos e desfazer tudo que foi feito. Veja o próximo exemplo.

<?php

        $db = new PDO("mysql:host=localhost;dbname=banco", "root", ""); // É recomendado que a instância do PDO seja passada por parâmetro.
    
    try {

        $db->beginTransaction();
        $db->exec("UPDATE pedidos SET compra = 5641");
        $db->exec("UPDATE clientes SET compra = 5641 ");
        $db->exec("INSERT INTO logística(compra) VALUES (5641)");
    
        $db->commit();
        // Caso tudo tenha dado certo
    
    } catch (Exception $e) {
            $connection->rollback();
            return false;
       // Caso não deu certo $db->rollback(); 
    }

Prepared Statements

Statements são comandos SQL pré-construídos e garantem que nenhuma query que foi preparada sofra um ataque SQL injection, ou seja, um ataque baseado na manipulação do código SQL pela inserção de informações em uma query.

  • Prepare

O PDO possui um método chamado prepare que, ao utilizá-lo, tem como idéia apenas iniciar a query e aguardar pela inclusão de valores posteriormente, ou sejam, o comando não será executado, apenas preparado para ser executado depois. O método prepare retorna um objeto da classe PDOStatement para que, quando utilizamos esse método, precisamos criar placeholders que serão substituídos pelos valores que queremos que estejam naquela query. Veja o próximo exemplo.

    public function on($connect) {
        $stm = $connect->prepare("SELECT * FROM `users` WHERE email = :email AND password = :password LIMIT 1");
        $stm->BindValue(":email", $this->email, PDO::PARAM_STR);
        $stm->BindValue(":password", $this->password, PDO::PARAM_STR);
    	$stm->execute();
    	return $stm -> fetch(PDO::FETCH_ASSOC);
	}

Neste caso, foi criada uma função on, que pode ser executada após se instanciar um objeto do tipo Login. A função on espera receber como atributo o $connect (veremos que $connect vai ter uma classe, onde tem uma função que contém uma instância de um objeto do tipo PDO).

Na primeira linha da função utilizamos o método prepare da classe PDO, o método prepare é utilizado com a ideia de apenas iniciar a query e aguardar pela inclusão de valores posteriormente.

No nosso caso a query é um comando MySQL de seleção que, em português, está dizendo: “Selecione tudo da(s) linha(s) da tabela users onde o campo email for igual ao parâmetro :email e o campo password for igual ao parâmetro :password, com o limite de uma linha.” Se o servidor de banco de dados preparar o comunicado com êxito, o prepare retorna um objeto PDOStatement que é armazenado em $stm. Se o servidor de banco de dados não pode se preparar com êxito, a declaração retorna FALSE.

  • BindValue

Tomando como base o último exemplo, após o prepare, temos acesso ao método BindValue do PDOStatement, a função dele é vincular um valor a um parâmetro. No nosso caso, temos que o parâmetro :email passa a ter o valor atual da nossa propriedade $email, bem como :password passa a ter o valor de $password. O PDO::PARAM_ define o tipo de dado que está sendo passado, confira TODOS.

    $stm -> BindValue(":email", $this -> email, PDO::PARAM_STR);
    $stm -> BindValue(":password", $this -> password, PDO::PARAM_STR);
    		
  • Execute

Em seguida, temos mais um método de PDOStatement, o execute, que como o nome indica, executa um declaração preparada.

    $stm -> execute();
  • Fetch

Por fim, retornamos o resultado do método fetch do PDOStatement. O método fetch retorna uma linha de um conjunto de resultados associado a um objeto PDOStatement. O PDO::FETCH_ determina a forma como o fetch vai retornar o resultado, confira http://php.net/manual/pt_BR/pdostatement.fetch.php.

    return $stm -> fetch(PDO::FETCH_ASSOC);

Arquivo Connect

<?php
    class Connect {
        protected static function start()
        {
            $pdo = new PDO('mysql:host=localhost;dbname=git-github', 'root', '');
            $pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
            return $pdo;
        }
    }
?>

No arquivo connect veremos que ele passa a ter uma classe, onde tem uma função que contém uma instância de um objeto do tipo PDO. Utilizamos o arquivo connect como uma classe para que ela possa ser atribuída como herança em outras classes. Veja o exemplo:

public function insert() {
            $connect = static::start();
            $stm = $connect->prepare("INSERT INTO users(name, email, phone, registry, semester, course, status) VALUES (:name, :email, :phone, :registry, :semester, :course, :status)");
            $stm->BindValue(":name", $this->name, PDO::PARAM_STR);
            $stm->BindValue(":email", $this->email, PDO::PARAM_STR);
            $stm->BindValue(":phone", $this->phone, PDO::PARAM_STR);
            $stm->BindValue(":registry", $this->registry, PDO::PARAM_INT);
            $stm->BindValue(":semester", $this->semester, PDO::PARAM_INT);
            $stm->BindValue(":course", $this->course, PDO::PARAM_STR);
            $stm->BindValue(":status", $this->status, PDO::PARAM_INT);
            return $stm->execute();
        }

Na primeira linha da nossa classe connect temos o static que é usado para definir que um método ou atributo em uma classe é estático. Isso significa que aquele método/atributo pertence à classe e não à uma instância dela e, por isso, pode ser acessado sem instânciar um novo objeto. Dentro da função start, instanciamos um novo objeto PDO, e armazenamos no $pdo. Os parâmetros passados são: a linguagem do banco de dados e o host, o nome do banco de dados, o usuário do bd e a senha, que no nosso caso está em branco. Daí utilizamos o método setAttribute para dizer o modo como erro vai ser reportado.