PHP orientado a objeto com MySQL e AJAX - Seleção de estado e cidade
Dica publicada em PHP / Introdução
PHP orientado a objeto com MySQL e AJAX - Seleção de estado e cidade
Hoje vou dar uma pincelada sobre algo que eu queria abordar faz tempo, mas como me afastei do PHP por conta da correria do trabalho no dia-a-dia, demorei... Mas nunca é tarde.
O exemplo é bem simples e clássico, solução pra um problema e desejo antigo: carregar dados numa página web sem refresh. Eu sempre ouvia falar do AJAX e, apesar de ter começado minha carreira fazendo sites, só fui vê-lo em 2006, mas a primeiras coisas que estou fazendo com ele pra testar foram só recentemente. E agora que entendi quero compartilhar - eu já até estudei o Adobe Flex e cada um (Flex e AJAX) tem suas vantagens, mas a principal do AJAX é ser mais leve, pois usa os próprios bons e velhos HTML, XML e JavaScript, sem precisar da instalação de plugins como o do Flash Player que o Flex precisa.
Como eu disse, é um exemplo simples e clássico de seleção de cidades, onde numa caixa você seleciona o estado desejado e na de baixo a cidade numa lista criada dinamicamente com as cidades de cada estado selecionado.
Pra começar, vamos à criação do nosso banco de dados no MySQL - eu peguei parte das tabelas de um banco no qual estou trabalhando, bem simples, foi um dump mesmo que fiz no phpMyAdmin do meu servidor on-line, já com os dados pro exemplo. Veja:
Criado o banco, vamos ao PHP. Eu usei o próprio Gedit do Ubuntu (se você usa o ambiente KDE do Kubuntu - o qual eu também tenho instalado por curiosidade, gostei, achei legal e muito bonito, embora não seja tão prático como o GNOME - use o Kate, mas tem que verificar a compatibilidade com UTF-8).
Voltando ao assunto, o primeiro arquivo que criamos é o dados.php, que contêm a classe de conexão ao banco e seleção da base, conforme abaixo:
Eu usei funções nativas e simples do PHP na conexão pra ficar mais legível. Ah, e se você notou, no PHP a classe deve ter o mesmo nome do arquivo que a contém.
Bem, agora vamos criar o arquivo cidades_obj.php, que será a nossa classe de objeto que representa e guarda os dados de cada cidade selecionada. Note que as propriedades do objeto são um espelho dos campos do banco de dados pra, também, ficar mais legível:
Ok, agora vamos à classe que pega os dados no banco e joga numa lista de objetos (ou seja, a lista com cada cidade representada pelo objeto que criamos acima). Lista de objetos é pra ficar mais bonitinho, mas é um array mesmo. O nosso arquivo é o cidades.php. No início dele temos as propriedades que usaremos como parâmetros e na função get_cidades usamos as funções clássicas pra trabalhar com querys do MySQL no PHP:
Já temos o objeto que trará nossos resultados, agora vamos ler o array gerado pela classe acima e escrever o nosso XML no arquivo cidades.xml.php. Note que no início do arquivo instanciamos o objeto da classe acima e mais abaixo usamos a biblioteca DOMDocument do PHP5 pra gerar o XML:
O resultado gerado pela função acima é um arquivo XML mesmo como este:
1
3
AFL
Alta Floresta
2
6
CVL
Cascavel
1
1
CBA
Cuiabá
1
4
LRV
Lucas do Rio Verde
1
5
ROO
Rondonópolis
1
2
SNP
Sinop
Por fim, vamos à nossa página com a seleção das cidades por estado. Eu dei ao arquivo o nome de idades.html.php seguindo o padrão que você deve ter percebido acima: ".xml.php" pra XML, "_obj.php" pra objeto (só criei com "_" por causa no nome da classe do objeto), só ".php" pra classe que pega os dados, e agora .html.php" pro arquivo que será visível mesmo - no meu ponto de vista este padrão ajuda a identificar pelo nome o propósito de de cada arquivo.
Bem, este último arquivo é o maior e mais complexo, onde vamos trabalhar com o AJAX mesmo, isso significa muito JavaScript, pois como o próprio nome já diz é Asynchronous Javascript And XML. Temos as funções todas no JavaScript para instanciar o AJAX, ler, fazer a consulta no nosso XML e imprimir os resultados na tela. Eis o código completo:
O HTML é normal. Preste atenção aos nomes dos elementos pra poder chamá-los no JavaScript e às variáveis que armazenam os dados. Aquela verificação do suporte ao AJAX é por conta das diferenças de padrões - muita gente ainda não acordou pra vida e anida usa Microsoft Internet Explorer 6, e bem desatualizado, por isso nossa página pode não funcionar direito pra eles; além do que esta verificação é necessária porque o Windows Internet Explorer, me refiro já a algo mais considerável, que é o IE7 e o IE8, trabalha com AJAX via ActiveX, enquanto o Firefox, o Safari e o Chrome têm isso um pouco mais 'nativo' (não verifiquei o Opera).
Seguindo, nas últimas linhas da função Dados note que passamos o parâmetro com o código da UF que é verifiado la no $_POST do arquivo XML fazendo o nosso filtro. E na função proccessXML lemos o resultado retornado do XML chamado na função anterior, e preenchemos os valores da lista de cidades de acordo com eles.
Eu ignorei a tabela de países e criei a lista de estados fixa no HTML pra agilizar o exemplo.
Enfim, é bem resumido, mas espero que tenha sido proveitoso. Se você quiser como ficou o meu exemplo funcionando, clique aqui: http://pedro-araujo.com/tarefas/cidades.html.php
Referências:
O exemplo é bem simples e clássico, solução pra um problema e desejo antigo: carregar dados numa página web sem refresh. Eu sempre ouvia falar do AJAX e, apesar de ter começado minha carreira fazendo sites, só fui vê-lo em 2006, mas a primeiras coisas que estou fazendo com ele pra testar foram só recentemente. E agora que entendi quero compartilhar - eu já até estudei o Adobe Flex e cada um (Flex e AJAX) tem suas vantagens, mas a principal do AJAX é ser mais leve, pois usa os próprios bons e velhos HTML, XML e JavaScript, sem precisar da instalação de plugins como o do Flash Player que o Flex precisa.
Como eu disse, é um exemplo simples e clássico de seleção de cidades, onde numa caixa você seleciona o estado desejado e na de baixo a cidade numa lista criada dinamicamente com as cidades de cada estado selecionado.
Pra começar, vamos à criação do nosso banco de dados no MySQL - eu peguei parte das tabelas de um banco no qual estou trabalhando, bem simples, foi um dump mesmo que fiz no phpMyAdmin do meu servidor on-line, já com os dados pro exemplo. Veja:
SET SQL_MODE="NO_AUTO_VALUE_ON_ZERO";
CREATE DATABASE `cidades` DEFAULT CHARACTER SET utf8 COLLATE utf8_general_ci;
USE `cidades`;
CREATE TABLE IF NOT EXISTS `cidades` (
`id_cidade` int(11) NOT NULL auto_increment,
`id_uf` int(11) NOT NULL,
`nome` varchar(35) NOT NULL,
`cep_principal` char(10) NOT NULL,
`is_capital` tinyint(1) NOT NULL default '0',
`sigla` varchar(3) NOT NULL,
PRIMARY KEY (`id_cidade`),
UNIQUE KEY `sigla` (`sigla`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8 AUTO_INCREMENT=7 ;
INSERT INTO `cidades` (`id_cidade`, `id_uf`, `nome`, `cep_principal`, `is_capital`, `sigla`) VALUES
(1, 1, 'Cuiabá', '78000-000', 1, 'CBA'),
(2, 1, 'Sinop', '78550-000', 0, 'SNP'),
(3, 1, 'Alta Floresta', '78580-000', 0, 'AFL'),
(4, 1, 'Lucas do Rio Verde', '78455-000', 0, 'LRV'),
(5, 1, 'Rondonópolis', '78700-000', 0, 'ROO'),
(6, 2, 'Cascavel', '85800-000', 0, 'CVL');
CREATE TABLE IF NOT EXISTS `paises` (
`id_pais` int(11) NOT NULL auto_increment,
`sigla` varchar(5) NOT NULL,
`nome` varchar(30) NOT NULL,
PRIMARY KEY (`id_pais`),
UNIQUE KEY `sigla` (`sigla`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8 AUTO_INCREMENT=2 ;
INSERT INTO `paises` (`id_pais`, `sigla`, `nome`) VALUES
(1, 'BRA', 'Brasil');
CREATE TABLE IF NOT EXISTS `uf` (
`id_uf` int(11) NOT NULL auto_increment,
`id_pais` int(11) NOT NULL,
`sigla` char(2) NOT NULL,
`nome` varchar(25) NOT NULL,
PRIMARY KEY (`id_uf`),
UNIQUE KEY `sigla` (`sigla`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8 AUTO_INCREMENT=4 ;
INSERT INTO `uf` (`id_uf`, `id_pais`, `sigla`, `nome`) VALUES
(1, 1, 'MT', 'Mato Grosso'),
(2, 1, 'PR', 'Paraná'),
(3, 1, 'MS', 'Mato Grosso do Sul');
CREATE DATABASE `cidades` DEFAULT CHARACTER SET utf8 COLLATE utf8_general_ci;
USE `cidades`;
CREATE TABLE IF NOT EXISTS `cidades` (
`id_cidade` int(11) NOT NULL auto_increment,
`id_uf` int(11) NOT NULL,
`nome` varchar(35) NOT NULL,
`cep_principal` char(10) NOT NULL,
`is_capital` tinyint(1) NOT NULL default '0',
`sigla` varchar(3) NOT NULL,
PRIMARY KEY (`id_cidade`),
UNIQUE KEY `sigla` (`sigla`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8 AUTO_INCREMENT=7 ;
INSERT INTO `cidades` (`id_cidade`, `id_uf`, `nome`, `cep_principal`, `is_capital`, `sigla`) VALUES
(1, 1, 'Cuiabá', '78000-000', 1, 'CBA'),
(2, 1, 'Sinop', '78550-000', 0, 'SNP'),
(3, 1, 'Alta Floresta', '78580-000', 0, 'AFL'),
(4, 1, 'Lucas do Rio Verde', '78455-000', 0, 'LRV'),
(5, 1, 'Rondonópolis', '78700-000', 0, 'ROO'),
(6, 2, 'Cascavel', '85800-000', 0, 'CVL');
CREATE TABLE IF NOT EXISTS `paises` (
`id_pais` int(11) NOT NULL auto_increment,
`sigla` varchar(5) NOT NULL,
`nome` varchar(30) NOT NULL,
PRIMARY KEY (`id_pais`),
UNIQUE KEY `sigla` (`sigla`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8 AUTO_INCREMENT=2 ;
INSERT INTO `paises` (`id_pais`, `sigla`, `nome`) VALUES
(1, 'BRA', 'Brasil');
CREATE TABLE IF NOT EXISTS `uf` (
`id_uf` int(11) NOT NULL auto_increment,
`id_pais` int(11) NOT NULL,
`sigla` char(2) NOT NULL,
`nome` varchar(25) NOT NULL,
PRIMARY KEY (`id_uf`),
UNIQUE KEY `sigla` (`sigla`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8 AUTO_INCREMENT=4 ;
INSERT INTO `uf` (`id_uf`, `id_pais`, `sigla`, `nome`) VALUES
(1, 1, 'MT', 'Mato Grosso'),
(2, 1, 'PR', 'Paraná'),
(3, 1, 'MS', 'Mato Grosso do Sul');
Criado o banco, vamos ao PHP. Eu usei o próprio Gedit do Ubuntu (se você usa o ambiente KDE do Kubuntu - o qual eu também tenho instalado por curiosidade, gostei, achei legal e muito bonito, embora não seja tão prático como o GNOME - use o Kate, mas tem que verificar a compatibilidade com UTF-8).
Voltando ao assunto, o primeiro arquivo que criamos é o dados.php, que contêm a classe de conexão ao banco e seleção da base, conforme abaixo:
class dados
{
//Propriedades/parâmetros do objeto de conexão
public $host = "localhost";
public $user = "cidades";
public $senha = "cidades";
public $db = "cidades";
public function get_conexao()
{
//Conecta ao MySQL e seleciona o banco de dados
$cnx = @mysql_connect($this->host, $this->user, $this->senha) or die("
@mysql_select_db($this->db, $cnx) or die("
}
}
?>
{
//Propriedades/parâmetros do objeto de conexão
public $host = "localhost";
public $user = "cidades";
public $senha = "cidades";
public $db = "cidades";
public function get_conexao()
{
//Conecta ao MySQL e seleciona o banco de dados
$cnx = @mysql_connect($this->host, $this->user, $this->senha) or die("
(!) Falha ao conectar ao banco de dados.");
@mysql_select_db($this->db, $cnx) or die("
(!) Falha ao selecionar banco de dados. ".str_replace('..', '.', mysql_error().'.').""); }
}
?>
Eu usei funções nativas e simples do PHP na conexão pra ficar mais legível. Ah, e se você notou, no PHP a classe deve ter o mesmo nome do arquivo que a contém.
Bem, agora vamos criar o arquivo cidades_obj.php, que será a nossa classe de objeto que representa e guarda os dados de cada cidade selecionada. Note que as propriedades do objeto são um espelho dos campos do banco de dados pra, também, ficar mais legível:
class cidades_obj
{
//Propriedades do objeto cidades
public $id_cidade;
public $id_uf;
public $nome;
public $cep_principal;
public $is_capital;
public $sigla;
}
?>
{
//Propriedades do objeto cidades
public $id_cidade;
public $id_uf;
public $nome;
public $cep_principal;
public $is_capital;
public $sigla;
}
?>
Ok, agora vamos à classe que pega os dados no banco e joga numa lista de objetos (ou seja, a lista com cada cidade representada pelo objeto que criamos acima). Lista de objetos é pra ficar mais bonitinho, mas é um array mesmo. O nosso arquivo é o cidades.php. No início dele temos as propriedades que usaremos como parâmetros e na função get_cidades usamos as funções clássicas pra trabalhar com querys do MySQL no PHP:
include_once("dados.php");
include_once("cidades_obj.php");
class cidades
{
//Propriedados/parâmetros do objeto de consulta de cidades
public $id_cidade = 0;
public $id_uf = 0;
public $nome = '';
public $cep_principal = '';
public $is_capital = false;
public $sigla = '';
//Contador de resultados
public $count = 0;
public function get_cidades()
{
//Seleciona cidades no banco
$sql = "SELECT * FROM cidades";
$sql .= " WHERE id_cidade > 0";
//Verifica parâmetros
if ($this->id_cidade > 0) $sql .= " AND id_cidade = ".$this->id_cidade;
if ($this->id_uf > 0) $sql .= " AND id_uf = ".$this->id_uf;
if (strlen($this->nome) > 0) $sql.= " AND nome LIKE '%".$this->nome."%'";
if (strlen($this->cep_principal) > 0) $sql.= " AND cep_principal = '".$this->cep_principal."'";
if ($this->is_capital == true) $sql .= " AND is_capital";
if (strlen($this->sigla) > 0) $sql.= " AND sigla = '".$this->sigla."'";
$sql .= " ORDER BY nome ASC";
//Conecta ao banco e abre a query
$dados = new dados();
$dados->get_conexao();
$rs = mysql_query($sql);
$this->count = 0;
//Passa resultados pra um array do objeto cidades
while ($reg = mysql_fetch_array($rs))
{
$cidade = new cidades_obj();
$cidade->id_cidade = $reg['id_cidade'];
$cidade->id_uf = $reg['id_uf'];
$cidade->nome = $reg['nome'];
$cidade->cep_principal = $reg['cep_principal'];
$cidade->is_capital = $reg['is_capital'];
$cidade->sigla = $reg['sigla'];
$a[] = $cidade;
$this->count++;
}
//Fecha conexão
mysql_close();
//Retorna lista de cidades
return $a;
}
}
?>
include_once("cidades_obj.php");
class cidades
{
//Propriedados/parâmetros do objeto de consulta de cidades
public $id_cidade = 0;
public $id_uf = 0;
public $nome = '';
public $cep_principal = '';
public $is_capital = false;
public $sigla = '';
//Contador de resultados
public $count = 0;
public function get_cidades()
{
//Seleciona cidades no banco
$sql = "SELECT * FROM cidades";
$sql .= " WHERE id_cidade > 0";
//Verifica parâmetros
if ($this->id_cidade > 0) $sql .= " AND id_cidade = ".$this->id_cidade;
if ($this->id_uf > 0) $sql .= " AND id_uf = ".$this->id_uf;
if (strlen($this->nome) > 0) $sql.= " AND nome LIKE '%".$this->nome."%'";
if (strlen($this->cep_principal) > 0) $sql.= " AND cep_principal = '".$this->cep_principal."'";
if ($this->is_capital == true) $sql .= " AND is_capital";
if (strlen($this->sigla) > 0) $sql.= " AND sigla = '".$this->sigla."'";
$sql .= " ORDER BY nome ASC";
//Conecta ao banco e abre a query
$dados = new dados();
$dados->get_conexao();
$rs = mysql_query($sql);
$this->count = 0;
//Passa resultados pra um array do objeto cidades
while ($reg = mysql_fetch_array($rs))
{
$cidade = new cidades_obj();
$cidade->id_cidade = $reg['id_cidade'];
$cidade->id_uf = $reg['id_uf'];
$cidade->nome = $reg['nome'];
$cidade->cep_principal = $reg['cep_principal'];
$cidade->is_capital = $reg['is_capital'];
$cidade->sigla = $reg['sigla'];
$a[] = $cidade;
$this->count++;
}
//Fecha conexão
mysql_close();
//Retorna lista de cidades
return $a;
}
}
?>
Já temos o objeto que trará nossos resultados, agora vamos ler o array gerado pela classe acima e escrever o nosso XML no arquivo cidades.xml.php. Note que no início do arquivo instanciamos o objeto da classe acima e mais abaixo usamos a biblioteca DOMDocument do PHP5 pra gerar o XML:
include_once("cidades.php");
//Chama objeto cidades passando parâmetro por UF
$cidades = new cidades();
$cidades->id_uf = $_POST["id_uf"];
$lst = $cidades->get_cidades();
//Verifica se o array tem resultados
if ($cidades->count > 0)
{
//Cria XML
$xml = new DOMDocument("1.0", "UTF-8");
$xml->preserveWhiteSpace = false;
$xml->formatOutput = true;
//Insere nó principal
$root = $xml->createElement("cidades");
//Varre o array
foreach($lst as $city)
{
//Atribui variáveis pra criar campos com o valor de cada registro
$id_uf = $xml->createElement("id_uf", $city->id_uf);
$id_cidade = $xml->createElement("id_cidade", $city->id_cidade);
$sigla = $xml->createElement("sigla", utf8_encode($city->sigla));
$nome = $xml->createElement("nome", utf8_encode($city->nome));
//Cria nó de registro
$cidade = $xml->createElement("cidade");
//Adiona campos com os valores
$cidade->appendChild($id_uf);
$cidade->appendChild($id_cidade);
$cidade->appendChild($sigla);
$cidade->appendChild($nome);
//Adiciona o registro ao nó prncipal
$root->appendChild($cidade);
}
//Fecha a TAG do nó principal
$xml->appendChild($root);
//Imprime o XML na tela
Header("Content-Type: text/xml");
echo $xml->saveXML();
}
?>
//Chama objeto cidades passando parâmetro por UF
$cidades = new cidades();
$cidades->id_uf = $_POST["id_uf"];
$lst = $cidades->get_cidades();
//Verifica se o array tem resultados
if ($cidades->count > 0)
{
//Cria XML
$xml = new DOMDocument("1.0", "UTF-8");
$xml->preserveWhiteSpace = false;
$xml->formatOutput = true;
//Insere nó principal
$root = $xml->createElement("cidades");
//Varre o array
foreach($lst as $city)
{
//Atribui variáveis pra criar campos com o valor de cada registro
$id_uf = $xml->createElement("id_uf", $city->id_uf);
$id_cidade = $xml->createElement("id_cidade", $city->id_cidade);
$sigla = $xml->createElement("sigla", utf8_encode($city->sigla));
$nome = $xml->createElement("nome", utf8_encode($city->nome));
//Cria nó de registro
$cidade = $xml->createElement("cidade");
//Adiona campos com os valores
$cidade->appendChild($id_uf);
$cidade->appendChild($id_cidade);
$cidade->appendChild($sigla);
$cidade->appendChild($nome);
//Adiciona o registro ao nó prncipal
$root->appendChild($cidade);
}
//Fecha a TAG do nó principal
$xml->appendChild($root);
//Imprime o XML na tela
Header("Content-Type: text/xml");
echo $xml->saveXML();
}
?>
O resultado gerado pela função acima é um arquivo XML mesmo como este:
Por fim, vamos à nossa página com a seleção das cidades por estado. Eu dei ao arquivo o nome de idades.html.php seguindo o padrão que você deve ter percebido acima: ".xml.php" pra XML, "_obj.php" pra objeto (só criei com "_" por causa no nome da classe do objeto), só ".php" pra classe que pega os dados, e agora .html.php" pro arquivo que será visível mesmo - no meu ponto de vista este padrão ajuda a identificar pelo nome o propósito de de cada arquivo.
Bem, este último arquivo é o maior e mais complexo, onde vamos trabalhar com o AJAX mesmo, isso significa muito JavaScript, pois como o próprio nome já diz é Asynchronous Javascript And XML. Temos as funções todas no JavaScript para instanciar o AJAX, ler, fazer a consulta no nosso XML e imprimir os resultados na tela. Eis o código completo:
O HTML é normal. Preste atenção aos nomes dos elementos pra poder chamá-los no JavaScript e às variáveis que armazenam os dados. Aquela verificação do suporte ao AJAX é por conta das diferenças de padrões - muita gente ainda não acordou pra vida e anida usa Microsoft Internet Explorer 6, e bem desatualizado, por isso nossa página pode não funcionar direito pra eles; além do que esta verificação é necessária porque o Windows Internet Explorer, me refiro já a algo mais considerável, que é o IE7 e o IE8, trabalha com AJAX via ActiveX, enquanto o Firefox, o Safari e o Chrome têm isso um pouco mais 'nativo' (não verifiquei o Opera).
Seguindo, nas últimas linhas da função Dados note que passamos o parâmetro com o código da UF que é verifiado la no $_POST do arquivo XML fazendo o nosso filtro. E na função proccessXML lemos o resultado retornado do XML chamado na função anterior, e preenchemos os valores da lista de cidades de acordo com eles.
Eu ignorei a tabela de países e criei a lista de estados fixa no HTML pra agilizar o exemplo.
Enfim, é bem resumido, mas espero que tenha sido proveitoso. Se você quiser como ficou o meu exemplo funcionando, clique aqui: http://pedro-araujo.com/tarefas/cidades.html.php
Referências:
- Ajax e PHP - Carregando dados sem refresh
- Gerar XML com PHP « Rafael Clares
- AJAX (programação) - Wikipédia, a enciclopédia livre