Populando selects de cidades e estados com AJAX (PHP e jQuery)

Nada pior do que deixar um campo de cidade como texto livre para os usuários. Os erros de português e digitação acabam com qualquer chance de um relatório fiel filtrado por localidade. Além do mais, é muito mais fácil pro próprio usuário poder escolher sua cidade em uma lista já estabelecida.

Com este artigo, pretendo mostrar um método fácil de implementar um select de estados que, via AJAX, popula o select de cidades. O frontend utiliza a biblioteca javascript jQuery e o script AJAX foi desenvolvido em PHP.

Abaixo, além do arquivo de exemplo, você também encontra para download um SQL completo com as tabelas de cidades e estados. Portanto, caso queira desenvolver para um outra linguagem, basta seguir a lógica apresentada aqui.

Banco de Dados

Antes de mais nada, precisamos criar a nossa base de cidades e estados. No seu banco MySQL, crie as tabelas cidades e estados. O arquivo SQL abaixo faz isso e ainda insere todos os registros necessários. Não sei se essa base está atualizada e/ou completa, mas é a que sempre utilizo em meus projetos e, até agora, ninguém reclamou!

O select de estados vai utilizar os campos cod_estados e sigla como dados. Já o de cidades, listará as cidades de acordo com o campo estados_cod_estados, exibindo o nome e tendo como valor cod_cidades.

O frontend

A idéia básica é a seguinte: o select de estados virá automaticamente populado com os registros cadastrados em nosso banco de dados. Toda vez que o usuário selecionar um estado diferente, o select de cidades será preenchido via AJAX fazendo uma consulta na nossa tabela de cidades, trazendo todos os registros relacionados ao estado.

Note que não vou me estender muito na conexão com o banco de dados. O ideal é fazer isso separado, com variáveis para o host, senha, usuário etc. Aqui vai tudo no mesmo arquivo, só pra agilizar o exemplo.

<?php
  $con = mysql_connect( 'localhost', 'root', 'root' );
  mysql_select_db( 'cadastro', $con );
?>
<label for="cod_estados">Estado:</label>
<select name="cod_estados" id="cod_estados">
  <option value=""></option>
  <?php
    $sql = "SELECT cod_estados, sigla
        FROM estados
        ORDER BY sigla";
    $res = mysql_query( $sql );
    while ( $row = mysql_fetch_assoc( $res ) ) {
      echo '<option value="'.$row['cod_estados'].'">'.$row['sigla'].'</option>';
    }
  ?>
  </option>
</select>

<label for="cod_cidades">Cidade:</label>
<select name="cod_cidades" id="cod_cidades">
  <option value="">-- Escolha um estado --</option>
</select>

O código acima representa o estado inicial da listagem de estados/cidades.

Populando o select

Vejamos agora como fica o javascript (jQuery) responsável pelo carregamento de dados no select de cidades. Vamos utilizar o padrão JSON para a saída do script PHP. Se você ainda não conhece esse padrão, vale a pena dar uma pesquisada - ele é extremamente útil para scripts AJAX, evitando aquele blocão HTML que todo mundo costuma usar como saída. O PHP fica responsável somente pelos dados enquanto o javascript trata e monta o HTML.

$(function(){
  $('#cod_estados').change(function(){
    if( $(this).val() ) {
      $('#cod_cidades').hide();
      $('.carregando').show();
      $.getJSON(
        'cidades.ajax.php?search=',
        {
          cod_estados: $(this).val(),
          ajax: 'true'
        }, function(j){
          var options = '<option value=""></option>';
          for (var i = 0; i < j.length; i++) {
            options += '<option value="' +
              j[i].cod_cidades + '">' +
              j[i].nome + '</option>';
          }
          $('#cod_cidades').html(options).show();
          $('.carregando').hide();
        });
    } else {
      $('#cod_cidades').html(
        '<option value="">-- Escolha um estado --</option>'
      );
    }
  });
});

O jQuery possui o método getJSON para o recebimento de dados no padrão citado acima. Como parâmetro enviamos o código do estado selecionado. O script PHP faz então uma pesquisa na tabela cidades e retorna os resultados relacionados com o código do estado em um array JSON.

Agora a última peça do nosso exemplo, o PHP responsável por retornar os dados encontrados, chamado na função getJSON:

header( 'Cache-Control: no-cache' );
header( 'Content-type: application/xml; charset="utf-8"', true );

$con = mysql_connect( 'localhost', 'root', 'root' ) ;
mysql_select_db( 'cadastro', $con );

$cod_estados = mysql_real_escape_string( $_GET['cod_estados'] );

$cidades = array();

$sql = "SELECT cod_cidades, nome
    FROM cidades
    WHERE estados_cod_estados=$cod_estados
    ORDER BY nome";
$res = mysql_query( $sql );
while ( $row = mysql_fetch_assoc( $res ) ) {
  $cidades[] = array(
    'cod_cidades'  => $row['cod_cidades'],
    'nome'      => $row['nome'],
  );
}

echo( json_encode( $cidades ) );

Como de praxe, deixo pra vocês o trabalho de efetuar melhorias em nosso script. E se, em um mesmo formulário, precisarmos de dois campos de endereço (um residencial e outro comercial)? Repetir o código nem pensar! O ideal seria uma função recebendo como parâmetro os IDs dos selects de estado e cidade. E que tal uma base de dados para bairros? Toda vez que uma cidade fosse escolhida, um select de bairros seria automaticamente populado. Ideias não faltam.

Espero que este post sirva de base para vocês melhorarem seus formulários de cadastro. E qualquer dúvida já sabem, utilizem os comentários.