Pular para o conteúdo

PHP orientado a objeto com MySQL e AJAX - Seleção de estado e cidade

Dica publicada em PHP / Introdução
Perfil removido removido
Hits: 24.477 Categoria: PHP Subcategoria: Introdução
  • Indicar
  • Impressora
  • Denunciar

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:

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');

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("
(!) 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;
  }
?>

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;
    }
  }
?>

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();
  }
?>

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:



  
    Selecione a cidade
    

    
    

    
    

  

  
    Selecione a cidade

    Tecnologia MySQL/OOP-PHP/AJAX
    



    

      UF: 
      
      


      Cidade: 
      
    

    Voltar


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:

Removendo PPAs graficamante no Ubuntu

Bitnami - Configure aplicações no servidor com poucos cliques

Como instalar o Slackware64 current no Virtualbox

Jogo Free-Spades: Espadas para Linux

Clonando e restaurando aplicativos no GNU/Linux

Apache e o teimoso charset ISO-8859-1 que não funciona em páginas PHP

Sintaxe das queries

Instalando o LAMP no Linux Mandriva 2008 Spring

Aviso de erro em arquivo de configuração do PHP

PHP - "Notice: Use of undefined constant..." [Resolvido]

#1 Comentário enviado por desv.carlos em 10/01/2011 - 12:12h
Muito bom o tutorial. É um dos poucos que achamos na internet e que realmente funciona.
#2 Comentário enviado por removido em 02/07/2011 - 11:37h
é o cara que faz jus ao nome "compartilhamento"
ta muito legal
#3 Comentário enviado por dastyler em 09/11/2013 - 05:53h
Olá Pedro,

Tentei implementar o seu código conforme o descrito na dica e deu erro de variavel indefinida no momento do retorno da função que traz os registros das cidades:

PHP Notice: Undefined variable: a in /var/www/html/select_php_ajax/classes/Cidades.php on line 58, referer: http://127.0.0.1/select_php_ajax/

#4 Comentário enviado por removido em 11/11/2013 - 09:00h
Cara, vou ter que dar uma olhada no código! Este exemplo é tão antigo que eu assim de cabeça não me lembro o que possa ser (deve ser uma variável não inicializada)! Mas "notice" não é um erro que impeça a execução, é apenas um alerta! Se você mudar a configuração de ERROR_REPORTING do PHP ou do próprio script ela deixa de aparecer.

Contribuir com comentário

Entre na sua conta para comentar.