Dsi 015 - poo e php - conexão com bancos de dados usando pdo
1. Tecnologia em Sistemas para Internet
Componente Curricular: Desenvolvimento para Servidores I
015 – Programação Orientada a Objetos e PHP
Conexão com Bancos de Dados usando PDO – PHP Data Objects
Professor: Jorge Luís Gregório | e-mail: jorge.gregorio@fatec.sp.gov.br
@jlgregorio81 Jorge Luís Gregório
2. Um pouco de história...
• A comunidade PHP sempre foi muito grande e muito ativa no mundo todo,
desta forma, o PHP cresceu com uma grande diversidade de bibliotecas
construídas para resolver problemas muito específicos, principalmente
quando o assunto é banco de dados.
• Existem diversos SGBDs no mercado, entre eles, os mais populares são
MySQL, Firebird, Postgres, entre outros. Desta forma, o PHP, no ápice do
seu crescimento fornecia bibliotecas diferentes com funções diferentes
para bancos de dados diferentes.
• Apesar de resolver problemas de desenvolvedores de bancos de dados
específicos, essa solução limitava o PHP, pois, ao ser considerada a
mudança do SGBD de uma aplicação, o desenvolvedor teria que adaptar
e/ou alterar todo a lógica de acesso ao banco de dados, pois o PHP
fornecia funções específicas para cada banco.
• Face a essa limitação, surge o PDO – PHP Data Objects
3. PDO – PHP Data Objects
• O PDO fornece uma API limpa e consistente que visa
unificar a maioria das características e funções
presentes nas extensões dos bancos de dados aos
quais o PHP suporta (Dall’Oglio, 2009, pág. 159)
• A PDO unifica as chamadas de métodos e, de acordo
com o banco de dados selecionado, faz a delegação
para a extensão específica do banco de dados.
• A PDO é totalmente orientada a objetos, diferente das
extensões específicas dos bancos de dados que ainda
possui o modelo procedural de chamada de funções.
4. PDO e as extensões para acesso a
bancos de dados do PHP
PDO
Extensão do
MySQL
Extensão do
PostgreSQL
Extensão do SQLite
Banco de dados
Aplicação Web
5. Ativando extensões no WAMP
• Por padrão, a extensão para conexão com banco de
dados MySQL vem ativa no Wamp. Caso deseja usar o
Postgres, é necessário ativar a exetensão.
– Clique no ícone do WAMP na bandeja do sistema,
selecione a opção PHP, depos PHP Extensions e ative as
opções php_pdo_pgsql e php_pgsql
• Para fazer manualmente, é necessário abrir o arquivo
php.ini na pasta do servidor Apache e retire o
comentário (símbolo ;) das linhas onde se encontra
digitado
extension=php_pdo_pgsql.dll
extension=php_pgsql
6. Testando se o PDO/PGSQL está ativo
• Depois de ativar as extensões do Postgres no PHP é
necessário fazer um teste para verificar se estão ativas;
• Crie um arquivo index.php e digite o seguinte código
nele:
phpinfo();
A função phpinfo mostrará todas as configurações do
servidor Apache e quais drivers de conexão foram
carregados. Localize as palavras PDO, pdo_pgsql e pgsql.
Se todas estiverem presentes, tanto o driver de conexão
com o Posgres quanto o PDO estão ativos.
8. Erro persistente!
• Caso os drivers/extensões não tenham sido carregados, provavelmente será
necessário editar o arquivo de configuração do Apache inserindo uma linha de
comando para carregar a DLL (Dynamic Link Library) de conexão com o Postgres.
• Essa DLL fica na pasta do wamp no seguinte caminho:
binphp<versão_do_php>libpq.dll
Abra o arquivo de configuração do Apache (httpd.conf) que fica na pasta do wamp no
seguinte caminho:
binapache<versão_do_apacheconfhttpd.conf
Logo após a última linha com o comando LoadModule, adicione a seguinte linha de
comando:
LoadFile “<raiz_do_wamp>/bin/php/<versão_do_php>/libpq.dll”
9. Criando um banco de dados
• Abra o PgAdmin (Postgres) e crie um banco de
dados com o nome ‘database’ (sem aspas)
• Clique no link para baixar o script para a
criação das tabelas:
– Clique aqui!
Powered by:
10. String de conexão
Banco de
Dados
String de conexão
FireBird new PDO(“firebird:dbname=C:base.GDB”,
“SYSDBA”, “masterkey”);
MySQL new
PDO(‘mysql:unix_socket=/tmp/mysql.sock;host=loc
alhost;port3306;dbname=livro’,’user’,’senha’)
Postgres new
PDO(‘pgsql:dbname=example;user=user;password=se
nha;host=localhost);
Adaptado de Dall’Oglio, 2009, pág. 160
11. Criando uma classe de conexão
namespace util;
use PDO;
/**
* Description of Connection
*
* @author JorgeLuis
*/
class Connection {
//..constantes para conexão com o Postgres
const DBNAME = 'database';
const USER = 'postgres';
const PASSWORD='postdba';
const HOST = 'localhost';
const PORT = 5432;
/**
* Retorna um objeto do tipo PDO pronto para executar instruções SQL no banco de dados
* @return PDO
* @throws utilPDOException
*/
public static function getConnection(){
try {
return new PDO("pgsql:dbname=".self::DBNAME.";user=".self::USER .
";password=".self::PASSWORD.";host=".self::HOST .
";port=".self::PORT);
} catch (PDOException $ex) {
throw $ex;
}
}
}
12. Inserindo dados
O método exec da classe PDO é capaz de executar uma instrução SQL diretamente no banco de
dados. O retorno do método é um número inteiro contendo a qtde de linhas linhas que foram
atualizadas no banco de dados.
require_once 'autoload.php';
use utilConnection;
try {
//..pega um objeto PDO
$db = Connection::getConnection();
} catch (Exception $ex) {
//..trata a exceção
echo $ex->getMessage() . " - " . $ex->getCode();
}
//..inserindo na tabela cidade
$linhas = $db->exec("insert into cidade (nome, uf, cep) values('Urânia','SP','15760000')");
if ($linhas > 0)
echo 'Dados inseridos!<br>';
else
echo 'Erro!';
13. Atualizando registros
O comando exec pode ser usado também para comandos de update:
require_once 'autoload.php';
use utilConnection;
//..pega um objeto PDO
$db = Connection::getConnection();
//..executa o comando update
$linhas = $db->exec("update cidade set nome='Londrina', uf='PR' where id_cidade = 1");
if($linhas > 0)
echo 'Dados atualizados com sucesso!<br>';
else
echo 'Erro!<br>';
14. Excluindo registros
require_once 'autoload.php';
use utilConnection;
//..pega um objeto PDO
$db = Connection::getConnection();
//..executa o comando delete
$linhas = $db->exec("delete from cidade where id_cidade = 1");
if($linhas > 0)
echo 'Dados excluídos com sucesso!<br>';
else
echo 'Erro!<br>';
15. Listando registros
• Para listar registros, isto é, executar uma consulta no banco de dados, o método
query deverá ser invocado. Se não houver dados que satisfaçam a consulta, o
retorno do método será false, caso contrário, será retornado um objeto
PDOStatement que é um resultset no formato de um array composto de outros
arrays.
require_once 'autoload.php';
use utilConnection;
//..pega um objeto PDO
$db = Connection::getConnection();
//..executa a consulta
$resultSet = $db->query('select * from cidade order by nome');
//..se houver resultSet, então itera-o mostrando os campos da consulta
if($resultSet){
foreach ($resultSet as $reg) {
echo $reg['id_cidade'] . ' - ';
echo $reg['nome'] . ' - ';
echo $reg['uf'] . ' - ';
echo $reg['cep'] . '<br>';
}
}
16. Listando registros
• É possível determinar outros formatos para
retornar os dados usando a função FETCH e
passando um parâmetro de acordo com a
tabela:
Parâmetros Descrição
PDO::FETCH_ASSOC Retorna um array indexado pelo nome da coluna (padrão)
PDO::FETCH_NUM Retorna um array indexado pela posição numérica da coluna
PDO::FETCH_BOTH Retorna um array indexado pelo nome da coluna e pela
posição numérica da mesma.
PDO::FETCH_OBJ Retorna um objeto (StdClass), de modo que cada coluna é
acessada como um propriedade.
Adaptado de Dall’Oglio, 2009, pág. 162
17. Listando registros - objetos
require_once 'autoload.php';
use utilConnection;
//..pega um objeto PDO
$db = Connection::getConnection();
//..executa a consulta
$resultSet = $db->query('select * from cidade order by nome');
if($resultSet){
while ($reg = $resultSet->fetch(PDO::FETCH_OBJ)){
echo $reg->id_cidade . ' - ' .
$reg->nome . ' - ' .
$reg->uf . ' - ' .
$reg->cep . '<br>';
}
}
18. Listando registros – índice numérico
require_once 'autoload.php';
use utilConnection;
//..pega um objeto PDO
$db = Connection::getConnection();
//..executa a consulta
$resultSet = $db->query('select * from cidade order by nome');
if($resultSet){
while ($reg = $resultSet->fetch(PDO::FETCH_NUM)){
echo $reg[0] . ' - ' .
$reg[1] . ' - ' .
$reg[2] . ' - ' .
$reg[3] . '<br>';
}
}
Este modo é recomendável para simplificar a referência aos atributos quando estes
são conhecidos. O parâmetro PDO::BOTH permite que os atributos sejam
referenciados tanto por índices numéricos, quanto por chaves.
19. Tratamento de erros
• A classe PDO possui um método chamado setAttribute ao qual é possível
configurar, entre outros recursos avançados, o modo de como os erros
gerados pelo objeto serão tratados.
• É recomendável configurar a classe de conexão para gerar exceções, pois
assim é possível lançar e/ou tratar os erros de forma mais elegante através
dos já conhecidos blocos try..catch.
• A exceção gerada é uma instância de PDOException, isto é, uma exceção
específica para erros de bancos de dados.
• Exemplo:
//..pega um objeto PDO
$db = Connection::getConnection();
//..seta o modo de erros
$db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
A partir de agora, o objeto $db irá lançar exceções, caso aconteçam.
20. Exemplo completo
try {
//..pega um objeto PDO
$db = Connection::getConnection();
//..seta o modo de erros
$db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
//..executa a consulta - nome da tabela cidade está errado – vai gerar erro!
$resultSet = $db->query('select * from cidades order by nome');
if ($resultSet) {
while ($reg = $resultSet->fetch(PDO::FETCH_BOTH)) {
echo $reg[0] . ' - ' .
$reg[1] . ' - ' .
$reg['uf'] . ' - ' .
$reg['cep'] . '<br>';
}
}
} catch (PDOException $ex) {
//..mostra a mensagem de erro
echo $ex->getMessage();
}
21. Modificando a classe Connection
• Para fazer com que a classe Connection retorne um objeto PDO pronto a gerar exceções, segue uma nova
proposta.
namespace util;
use PDO;
/**
* Description of Connection
*
* @author JorgeLuis
*/
class Connection {
//..constantes para conexão com o Postgres
const DBNAME = 'database';
const USER = 'postgres';
const PASSWORD='postdba';
const HOST = 'localhost';
const PORT = 5432;
/**
* Retorna um objeto do tipo PDO pronto para executar instruções SQL no banco de dados
* @return PDO
* @throws utilPDOException
*/
public static function getConnection(){
try {
//..pega um objeto PDO
$connection = new PDO("pgsql:dbname=".self::DBNAME.";user=".self::USER.
";password=".self::PASSWORD.";host=".self::HOST.";port=".self::PORT);
//..configura para gerar exceções sempre que um erro ocorrer
$connection->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
//..retorna o objeto PDO;
return $connection;
} catch (PDOException $ex) {
throw $ex;
}
}
}
22. Exemplo de tratamento de erros
require_once 'autoload.php';
use utilConnection;
try {
//..pega um objeto PDO
$db = Connection::getConnection();
//..executa a consulta - nome da tabela cidade está errado – vai gerar Exceção
$resultSet = $db->query('select * from cidades order by nome');
if ($resultSet) {
while ($reg = $resultSet->fetch(PDO::FETCH_BOTH)) {
echo $reg[0] . ' - ' .
$reg[1] . ' - ' .
$reg['uf'] . ' - ' .
$reg['cep'] . '<br>';
}
}
} catch (PDOException $ex) {
echo $ex->getMessage();
}
23. Transações
Uma transação representa uma interação entre a
aplicação e o sistema de banco de dados tratada de
forma única e independente. Pense em uma
transação financeira de transferência de fundos.
Este tipo de transação necessita de um grande
cuidado pois, do contrário, uma conta pode ser
debitada e a outra não ser creditada, ou vice-versa.
Uma transação é uma sequência de trabalho que
tem um ponto de início e de fim bem definidos [...]”
DALL’OGLIO, 2009, pág. 206
24. Propriedades de Transações - ACID
• Atomicidade: uma transação pode envolver uma ou mais interações com
o banco de dados, porém deve ser vista como uma única operação. Se
uma delas falhar, os dados não refletirão o mundo real então, toda a
operação deverá ser revertida (rollback). Executa tudo ou nada. Sem
operações incompletas.
• Consistêcia: após a transação, os dados devem permanecer íntegros, ou
seja, com todas as suas restrições (chave primária, check, etc) intactas.
• Isolamento: as transações devem ser executadas isoladas de outras
transações, pois pode ser que um registro seja acessado por diversas
transações ao mesmo tempo (venda de um produto). O resultado da
transação só deve ser visível após o seu término.
• Durabilidade: deve haver a garantia de que os dados serão persistentes
após o término da transação, ou seja, ela não será desfeita e/ou perdida
por qualquer que seja o motivo.
26. Exemplo
• No banco de dados, a tabela pessoafisica é uma
especialização da tabela pessoa, ou seja, para
cadastrar uma pessoa física é necessário primeiro
a inserção dos dados na tabela pessoa e, após
obter o nº do id inserido, é necessário inserir o
registro específico na tabela pessoafisica.
• Se qualquer uma das operações falhar no
momento da confirmação (commit), o sistema
deverá cancelar a transação (rollback).
• Os dados deverão ser inseridos nas duas tabelas
ou em nenhuma (atomicidade).
27. Código-fonte comentado
require_once 'autoload.php';
use utilConnection;
try{
//..pega uma conexão
$connection = Connection::getConnection();
//..inicia a transação
$connection->beginTransaction();
//..inicia as operações da transação
//..executa a instrução SQL para inserção em pessoa
$connection->exec("insert into pessoa(nome, id_cidade) values ('Peter Parker', 2);");
echo 'Pessoa Inserida! <br>';
//..pega o id do registro inserido em pessoa para inserí-lo em pessoafisica
$id = $connection->lastInsertId("seq_pessoa");
echo "ID inserido: $id <br>";
//..executa a instrução SQL para a inserção em pessoafisica
$connection->exec("insert into pessoafisica values ($id, 'M', '111','222','Rua XYZ','15999000');");
echo 'Pessoa Física Inserida! <br>';
//..confirma a transação
$connection->commit();
}
catch (PDOException $ex){
//..Se gerar exceção, mostra o erro e executa um rollback.
echo 'Erro na transação: ' . $ex->getMessage() . '<br>';
$connection->rollBack();
}
Dica: Para verificar se o controle de transação está realmente dando certo, crie um erro em uma
das instruções SQL, na primeira ou na segunda. Com erros, a lógica acima cairá no bloco catch e o
rollback será executado desfazendo as alterações.
28. Atividade Prática
• Criar uma aplicação simples que cadastre e liste
os dados de um formulário
• Dados:
– Nome (campo de texto)
– Sexo (campo select)
– Formação (campo select)
• Ensino fundamental
• Ensino médio
• Superior
• Pós graduação
• Outros
29. Mapeamento Objeto-Relacional
• Persistência
– Persistir quer dizer perdurar, resistir ao tempo. Em
uma aplicação, persistir significa fazer com que os
objetos do modelo de negócio sejam “gravados” em
um local externo à aplicação e que, principalmente,
permita sua posterior recuperação.
– Objetos existem apenas durante um certo período de
tempo dentro dos limites da aplicação, desta forma, é
extremamente importante seu armazenamento.
30. Mundo OO vs Mundo Relacional
[...] existem diversos conceitos da orientação a objetos
para os quais o modelo relacional simplesmente não
oferece suporte, então temos uma diferença de conceito.
Os bancos de dados foram construídos para armazenar
dados, ao passo que, no paradigma orientado a objetos,
além de dados (atributos), temos comportamento
(métodos) e outras estruturas complexas. Dessa forma, é
necessário utilizar alguma ferramenta de compatibilidade
entre os dois modelos, que geralmente implementam
algum técnica de mapeamento objeto-relacional. [...]
DALL’OGLIO, 2009, pág. 222
31. Mundo OO vs Mundo Relacional
• Mundo Relacional
– Tabelas: entidade que
armazena dados
– Registros: cada dado ou
conjunto de dados de uma
tabela real ou gerada por
SQL
– Chaves primárias:
responsáveis por:
• Fazer com que um registro
seja único
• Fazer com que as tabelas se
relacionem através de
ligação com chaves
estrangeiras
• Mundo OO
– Objetos: armazenam dados
e são capazes de realizar
tarefas
– Listas: conjuntos de
objetos
– Os relacionamentos são
feitos por associações:
• Associação simples
• Composição
• Agregação
32. Mundo OO vs Mundo Relacional
• Padrões para mapeamento objeto-relacional:
– Identity Field: campo autoincremento para chave primária/estrangeira
– Foreign Key Mapping: composição (relacionamento “forte” todo/parte) no
mundo OO pode ser realizada através de chaves estrangeiras.
– Association Table Mapping: agregação (relacionamento “fraco” todo/parte )
também pode ser mapeado como chaves estrangeiras.
– Herança
• Single Table Inheritance: as classes são mapeadas para uma única tabela contendo todos os
atributos comuns da hierarquia.
• Concrete Table Inheritance: as classes são mapeadas para diversas tabelas, cada classe possui
uma tabela de forma independente.
• Class Table Inheritance: e criada uma tabela para cada classe na estrutura da hierarquia. Após
isso, cria-se uma generalização/especialização através de pares de chaves primárias.
Maiores detalhes em Dall’Oglio, pág. 222-231.
33. DAO – Data Access Object
• O padrão de projeto DAO propõe a criação de uma camada de
acesso a dados independente de outras camadas da aplicação, ou
seja, os objetos DAO possui a responsabilidade de persistir objetos
em bancos de dados, realizando, o mapeamento objeto-relacional
(Object-Relational Mapping).
• Além da persistência (inserts e updates), os objetos DAO também
podem recuperar dados através de consultas SQL e retornar apenas
objetos de negócio (model) para a aplicação.
• Vantagens:
– Isolar a lógica de acesso e manipulação de dados de outros
componentes do sistema melhora a reusabilidade, escalabidade e
manutenabilidade do código.
34. DAO – Data Access Object
model
Classe1 Classe2
* *
Classe3 Classe4
1 *
+findById()
+persist()
+query()
+delete()
DAO
Banco de
dados
Objetos model Instruções SQL
Dados
Objetos
35. Exemplo
• Baixar o código-fonte comentado no link
abaixo:
– Clique aqui!
Powered by:
38. Discussão – métodos DAO
• Os objetos DAO devem implementar a interface Idao, então,
surgem os seguintes métodos:
– persist($modelObject)
• Insere e/ou atualiza um registro no banco de dados mediante um objeto
model informado. O objeto deverá ser mapeado em dados relacionais e
persistido no Banco de Dados.
– delete($id)
• Exclui um registro baseado no id informado
– query($field = null, $like=null)
• Realiza uma query do tipo “selecionar tudo” ou usando um campo e uma
cláusula ‘like’ como filtros. Retorna um ArrayObject (array de objetos)
– findById($id)
• Recupera um registro mediante o id informado, mapeia-o em objeto e retorna
para a aplicação.
39. O que vem depois???
• Agora que a camada DAO está pronta, é
necessário integrá-la a sua aplicação.
• MVC – Model/View/Controller
– Padrão de Projeto (Design Pattern) que divide uma
aplicação em 3 camadas distintas.
– O padrão DAO combina muito bem com o MVC.
40. Bibliografia recomendada
DALL’OGLIO, Pablo. Capítulo 3: Manipulação de dados. In: ________. PHP:
programando com orientação a objetos. 2. ed. São Paulo: Novatec, 2009. p. 154-220.
Manual do PHP. PHP Data Objects. Disponível em <http://php.net/manual/pt_BR/
book.pdo.php> Acesso em 20 jan. 2015.
Manual do PHP. PHP::setAttribute. Disponível em <http://php.net/manual/pt_BR/
pdo.setattribute.php> Acesso em 20 jan. 2015.