Olá pessoal, neste artigo será demonstrado como popular selects através de requisições AJAX a scripts em PHP, retornando os dados no formato JSON. Como exemplo, será construída uma página contendo listagens de Países, Estados e Cidades inseridas em selects, que serão filtrados conforme a opção selecionada.

Para construirmos esse exemplo, será necessário o seguinte ambiente de desenvolvimento, na seção links seguem endereço para downloads:

  • Pacote WampServer (pode ser outro da preferência do leitor);
  • Biblioteca jQuery (atualmente na versão 1.9.1);
  • Editor de texto (Bloco de Notas, Notepad++, Dreanweaver, etc.);

Será utilizado CSS para melhorar o visual da única página index.php, mas não serão detalhadas suas funcionalidades, pois o mesmo não faz parte do objetivo desse artigo, o nome do arquivo será estilo.css.

Para executarmos as requisições AJAX no formato JSON, será utilizada a função $.getJSON(), lembrando que ela pertence à biblioteca jQuery. Então se torna indispensável linkar na página o caminho para essa biblioteca.

Visando a simplicidade, do artigo será criado apenas um script de consulta PHP, consulta.php, onde através do método GET serão diferenciadas as funções de consultas (PAIS, ESTADO e CIDADE). Essas funções irão retornar os dados em JSON, para posteriormente serem consumidos pela função $.getJSON().

Todos os dados estão gravados em uma base de dados “atlas” no MySQL, o que torna necessário configurar uma conexão com o banco para executarmos consultas. Será criado um arquivo conexao.php contendo o script de conexão em PDO.

Como exemplo, serão adicionados na página index.php 3 selects, onde, conforme for selecionado um País, serão carregados os estados daquele país no select cmbEstado, quando selecionado um estado, serão carregadas as cidades daquele estado no select cmbCidade, ou seja, um filtro bem dinâmico.

O nosso banco irá conter:

  • Todos os Países do mundo;
  • Todos Estados (somente do Brasil);
  • Todas as Cidades (somente do Brasil);

Observação: Por motivos obvieis, não foram cadastrados todos os estados de todos os países do mundo, o mesmo vale para cidades. No banco existe um cadastro aproximado de todos os Países, todos os estados do Brasil e aproximadamente todas as cidades do Brasil, mas já serve como exemplo para esse artigo.

Antes de iniciar o desenvolvimento, é interessante organizar os arquivos dentro da pasta principal do projeto para melhor entendimento e organização do exemplo.

Organização das pastas e arquivos
Figura 1. Organização das pastas e arquivos

Como mencionado acima, todas as informações estão gravadas em uma base de dados “atlas” no MySQL, abaixo segue o script para criação do banco e das tabelas.

CREATE DATABASE IF NOT EXISTS;

CREATE TABLE IF NOT EXISTS `pais` (
  `sigla` char(2) NOT NULL,
  `iso3` char(3) NOT NULL,
  `numcode` smallint(6) NOT NULL,
  `nome` varchar(255) NOT NULL,
  PRIMARY KEY (`sigla`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8;

CREATE TABLE IF NOT EXISTS `estado` (
  `id` int(2) unsigned zerofill NOT NULL AUTO_INCREMENT,
  `sigla` varchar(2) NOT NULL DEFAULT '',
  `nome` varchar(20) NOT NULL DEFAULT '',
  `pais` char(2) DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=MyISAM AUTO_INCREMENT=31 DEFAULT CHARSET=utf8;

CREATE TABLE IF NOT EXISTS `cidade` (
  `id` int(4) unsigned zerofill NOT NULL AUTO_INCREMENT,
  `uf` varchar(4) NOT NULL DEFAULT '',
  `nome` varchar(50) NOT NULL DEFAULT '',
  UNIQUE KEY `id` (`id`),
  KEY `id_2` (`id`)
) ENGINE=MyISAM AUTO_INCREMENT=9715 DEFAULT CHARSET=utf8;
Listagem 1. Instruções SQL para criar a base de dados

Abaixo seguem os scripts em PHP.

conexao.php

Esse script possui uma função Conectar(), que retorna uma conexão PDO devidamente configurada.

<?php
function Conectar(){
	try{
		$opcoes = array(PDO::MYSQL_ATTR_INIT_COMMAND => 'SET NAMES UTF8');
		$con = new PDO("mysql:host=localhost; dbname=atlas;", "root", "011224", $opcoes);
		return $con;
	} catch (Exception $e){
		echo 'Erro: '.$e->getMessage();
		return null;
	}
}
?>
Listagem 2. Script para conexão com o banco de dados

consulta.php

Nesse script está toda a lógica das consultas que serão consumidas na página principal através das requisições AJAX. Poderíamos criar um script para cada consulta (PAÍS, ESTADO e CIDADE), mas usando um SWITCH podemos selecionar qual consulta será disparada dentro do mesmo script. Conforme a requisição, também será enviado um parâmetro para a instrução SQL que também é tratado nesse SWITCH.

Uma vez selecionada uma das funções de consulta, a mesma irá retornar os dados no formato JSON, atendendo assim o tipo de requisição feita.

<?php
require_once('conexao.php');
$opcao = isset($_GET['opcao']) ? $_GET['opcao'] : '';
$valor = isset($_GET['valor']) ? $_GET['valor'] : ''; 
if (! empty($opcao)){	
	switch ($opcao)
	{
		case 'pais':
			{
				echo getAllPais();
				break;
			}
		case 'estado':
			{
				echo getFilterEstado($valor);
				break;
			}
		case 'cidade':
			{
				echo getFilterCidade($valor);
				break;
			}
	}
}

function getAllPais(){
	$pdo = Conectar();
	$sql = 'SELECT sigla, nome FROM pais';
	$stm = $pdo->prepare($sql);
	$stm->execute();
	sleep(1);
	echo json_encode($stm->fetchAll(PDO::FETCH_ASSOC));
$pdo = null;	
}

function getFilterEstado($pais){
	$pdo = Conectar();
	$sql = 'SELECT sigla, nome FROM estado WHERE pais = ?';
	$stm = $pdo->prepare($sql);
	$stm->bindValue(1, $pais);
	$stm->execute();
	sleep(1);
	echo json_encode($stm->fetchAll(PDO::FETCH_ASSOC));
$pdo = null;	
}

function getFilterCidade($estado){
	$pdo = Conectar();
	$sql = 'SELECT nome FROM cidade WHERE uf = ?';
	$stm = $pdo->prepare($sql);
	$stm->bindValue(1, $estado);
	$stm->execute();
	sleep(1);
	echo json_encode($stm->fetchAll(PDO::FETCH_ASSOC));
$pdo = null;		
}
?>
Listagem 3. Script para consultas no banco de dados

Observações: Foi utilizada a função sleep() somente para atrasar o retorno da função em 1 segundo, assim será exibido na tela uma mensagem de aviso para usuário. Não é aconselhável usar esta prática para o desenvolvimento, uma vez que o próprio processamento na WEB já acarreta certo atraso no retorno das informações.

estilo.css

Para deixar o visual da página index.php mais agradável, será utilizado CSS. Para o leitor que se interessar, no portal DevMedia existem vários cursos sobre o assunto, pois o mesmo não será aprofundado neste artigo.

*{ margin: 0; padding: 0; font-family:Verdana, Geneva, sans-serif;} 

p{ text-align:center;}

select{ cursor: pointer; }

label{ color:#000000; font-weight: bold; font-size: 15px; 
line-height: 40px; margin: 20px;}

.destaque{ color: #900; font-weight: bold; font-size: 20px; 
line-height: 50px; }

.botao{ width: 100px; height: 25px; border-radius: 10px; 
border:2px solid #900; cursor:pointer; }

.mensagem{ color: #333; font-size: 15px; font-weight: 
bold; margin: 10px;}

#conteudo{ width: 900px; height: 300px; margin: 10px auto; 
border-radius: 10px; box-shadow: 5px 5px 5px #990000; 
background-color: #CCC; border: 1px solid #990000;}
Listagem 4. Folha de estilo CSS

index.php

Para finalizar, vamos agregar todos os scripts nesse arquivo, pois será ele a interface final com o usuário. Pelo fato do arquivo ser um pouco extenso, serão abordadas somente as partes de interesse para o funcionamento do exemplo.

<link rel="stylesheet" type="text/css" href="css/estilo.css">
<script type="text/javascript" src="js/jquery-1.8.2.js"></script>
Listagem 5. Linkando CSS e jQuery
<body>
   <div id="conteudo">
   		<p><span class="destaque">Populando selects 
   		usando AJAX + JSON + PHP</span></p>
        <hr />
        <br/>
        
        <div id="pais">
          <label>Selecione o País:</label>
          <select id="cmbPais">
              <option>Carregar Paises</option>
          </select>
          <input type="button" value="Carregar Pais" 
          id="btnPais" class="botao"/>
        </div>

        <div id="estado">
          <label>Selecione o Estado:</label>
          <select id="cmbEstado">
              <option>Carregar Estados</option>
          </select>
        </div>
        
        <div id="cidade">
          <label>Selecione a Cidade:</label>
          <select id="cmbCidade">
              <option>Carregar Cidades</option>
          </select>
        </div>
        
        <hr />
        <p><span class="destaque">Mensagens:
        </span></p>
        <div id="mensagem">
        	
        </div>
   </div>
</body> 
Listagem 6. Códigos HTML do corpo da página
Layout da página index.php
Figura 2. Layout da página index.php
<script type="text/javascript">
$(document).ready(function(){
	
	<!-- Carrega os Paises -->
	$('#btnPais').click(function(e){
		$('#btnPais').hide();
		$('#mensagem').html('<span class="mensagem">Aguarde, 
		carregando ...</span>');  
		
		$.getJSON('consulta.php?opcao=pais', function (dados){
			
		   if (dados.length > 0){ 	
			  var option = '<option>Selecione o País
			  </option>';
			  $.each(dados, function(i, obj){
				  option += '<option value="'+obj.sigla+'"
				  >'+obj.nome+'</option>';
			  })
			  $('#mensagem').html('<span class="mensagem"
			  >Total de paises encontrados.: '+dados.length+'</span>'); 
			  $('#cmbPais').html(option).show();
		   }else{
			   Reset();
			   $('#mensagem').html('<span class="mensagem"
			   >Não foram encontrados paises!</span>');
		   }
		})
	})
	
	<!-- Carrega os Estados -->
	$('#cmbPais').change(function(e){
		var pais = $('#cmbPais').val();
		$('#mensagem').html('<span class="mensagem">Aguarde, 
		carregando ...</span>');  
		
		$.getJSON('consulta.php?opcao=estado&valor='+pais, 
		function (dados){ 
		
		   if (dados.length > 0){	
			  var option = '<option>Selecione o Estado
			  </option>';
			  $.each(dados, function(i, obj){
				  option += '<option value="'+obj.sigla+'"
				  >'+obj.nome+'</option>';
			  })
			  $('#mensagem').html('<span class="mensagem">
			  Total de estados encontrados.: '+dados.length+'</span>'); 
		   }else{
			  Reset();
			  $('#mensagem').html('<span class="mensagem">
			  Não foram encontrados estados para esse país!</span>');  
		   }
		   $('#cmbEstado').html(option).show(); 
		})
	})
	
	<!-- Carrega as Cidades -->
	$('#cmbEstado').change(function(e){
		var estado = $('#cmbEstado').val();
		$('#mensagem').html('<span class="mensagem">Aguarde, 
		carregando ...</span>');  
		
		$.getJSON('consulta.php?opcao=cidade&valor='+estado, 
		function (dados){
			
			if (dados.length > 0){ 	
				var option = '<option>Selecione a 
				Cidade</option>';
				$.each(dados, function(i, obj){
					option += '<option>'+obj.nome+'</option>';
				})
				$('#mensagem').html('<span 
				class="mensagem">Total de cidades encontradas.: 
				'+dados.length+'</span>');
			}else{
				Reset();
				$('#mensagem').html('<span 
				class="mensagem">Não foram encontradas 
				cidades para esse estado!</span>');  
			}
			$('#cmbCidade').html(option).show();
		})
	})
	
	<!-- Resetar Selects -->
	function Reset(){
		$('#cmbPais').empty().append('<option>Carregar Países</option>>');
		$('#cmbEstado').empty().append('<option>Carregar Estados</option>>');
		$('#cmbCidade').empty().append('<option>Carregar Cidades</option>');
	}
});
</script>
Listagem 7. Códigos JavaScript para requisições

Como todas as chamadas são iguais, é interessante explicar alguns pontos importantes desse código JavaScript para que todas sejam entendidas.

Observem que para carregar os países, existe um button com texto “Carregar Pais”. No evento Click desse botão será dispara a requisição AJAX no formato JSON. Antes dessa ação, é ocultado o button e exibida uma mensagem para o usuário “Aguarde, carregando ...”.

Em seguida, é executada a requisição $.getJSON(URL, function(retorno)) que recebe 2 parâmetros:

  • URL para onde será enviada a requisição, opcionalmente pode-se adicionar parâmetros nesse URL, com será o nosso caso;
  • Uma função callback que recebe como parâmetro os dados recebidos da requisição, isso se a mesma for bem sucedida.

Observação: Essa requisição $.getJSON() é uma forma abreviada da requisição $.ajax().

O próximo passo é tratar as informações que retornaram do servidor dentro da função callback. Primeiro verificando se existem dados, caso existam, através de um laço de iteração carrega os dados criando “” dinamicamente para o select. Ao final do laço, exibe uma mensagem com a quantidade de registros retornados do servidor e inclui os dados que foram formatados dinamicamente dentro do laço de iteração.

Caso não seja retornado nenhum dado pelo servidor, é exibida uma mensagem informando que “Não foram encontrados países!” e executada a função Reset(), essa função tem a finalidade de limpar todas as options dos selects e adicionar somente a opção inicial, nesse caso, “Carregar Países”.

Observem que as outras 2 chamadas são exatamente iguais, alterando somente a forma como são disparadas e os parâmetros passados:

  1. Carrega todos os países no evento “Click” do button;
  2. Carrega os estados a partir do evento “Change” do select cmbPais, onde conforme o país selecionado, este será enviado como parâmetro para instrução SQL da função getFilterEstado().
  3. Carrega as cidades a partir do evento “Change” do select cmbEstado, onde conforme o estado selecionado, este será enviado como parâmetro para instrução SQL da função getFilterCidade().

Agora podemos testar os filtros, para iniciar basta pressionar o botão Carregar País.

Página inicial sem filtros ativos
Figura 3. Página inicial sem filtros ativos
Select cmbPais com países carregados
Figura 4. Select cmbPais com países carregados
País selecionado e select cmbEstado carregado
Figura 5. País selecionado e select cmbEstado carregado
Estado selecionado e select cmbCidade carregado
Figura 6. Estado selecionado e select cmbCidade carregado
Todas as cidades do estado de São Paulo
Figura 7. Todas as cidades do estado de São Paulo

Bom, pessoal, neste artigo foi demonstrado um exemplo simples de como executar requisições AJAX para o formato JSON. Esse tipo de requisição pode tornar a experiência do usuário com a página mais interessante e agradável, pois como foi visto, não existe o típico refresh para carregar os dados nos devidos selects. Existem outras variações desse tipo de requisição, no site oficial do jQuery existe uma vasta documentação sobre essa e diversas outras funções.

Foi possível observar também que, sabendo trabalhar com os eventos do select, pode-se deixar os filtros bem dinâmicos.