17 de novembro de 2009

29Populando 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.

29 leitores comentaram este artigo

  • 04/12/2009
    13:50

    Ricardo Kruger escreveu:

    Excelente este código, ajudou em muito a funcionalidade de meu sistema de cadastro o qual estou desenvolvendo.

    Assim que tiver um melhora no meu código, irei postar aqui uma atualização, como por exemplo o de bairros.

    Abraços

    Responder

  • 07/12/2009
    11:24

    Alexandre Broggio escreveu:

    Vlw pelo post ^_^

    Responder

  • 10/12/2009
    12:38

    Ricarte A. Barros escreveu:

    Bem enxuto! nota 10!

    Responder

  • 05/01/2010
    00:48

    Felipe escreveu:

    Cara bom de mais esse seu script, ele funciona 100% na minha maquina local , mas quando faço o FTP ele não lista as cidades, a conexão com o banco esta correta, sei lá pq não está funcionando..

    vc tem alguma dica pra mim sobre oq pode estar acontecendo.???

    se quiser ver é só acessar www.familyturismo.com

    valew
    abraços

    Responder

  • 05/01/2010
    09:37

    Davi Ferreira escreveu:

    Fala aí, Felipe.

    Cara, dei uma olhada lá no código-fonte do seu site e não consegui achar nenhuma referência ao javascript que carrega as cidades ou até mesmo do jquery. Não é aquele na home? Tá subindo certinho?

    Abrá!

    Responder

  • 18/01/2010
    14:40

    Luiz Bezerra escreveu:

    O meu na hr que eu subo tbm nao lista as cidades.

    Responder

  • 18/01/2010
    15:10

    Luiz Bezerra escreveu:

    obs:
    Versão do PHP 5.2.12
    Versão do MySQL 5.0.85-community

    Responder

  • 20/01/2010
    10:50

    tiago escreveu:

    como se faz esses campos de códigos, com a numeração das linhas ao lado?
    valeu

    Responder

  • 21/01/2010
    04:53

    tiago escreveu:

    Ah, esqueci de especificar melhor:
    "como se faz esses campos de códigos, com a numeração das linhas ao lado?", esses do seu blog, onde você mostra os códigos. Gostaria de fazer um blog também e colocar isso... Obrigado.

    Responder

  • 22/01/2010
    14:10

    tiago escreveu:

    outra coisa, na hora de carregar as cidades no mysql, algumas entraram com erros, como "BRASILÉIA", "TARAUACÁ", "JORDÃO", etc... sabe como faço pra nao entrar com esses caracteres?? Valeu

    Responder

  • 25/01/2010
    08:50

    Bruno escreveu:

    Aqui eu utilizei o script para carregar um combobox com subcategoria de produtos, ta dando problema em nomes com acento, por exemplo Câmera Digital, só aparece a letra C
    se eu retirar o acento aparece toda a palavra

    como faço para aparecer com acento?

    Responder

  • 07/02/2010
    15:06

    Davi Ferreira escreveu:

    Tem que se ligar que a base de dados está como UTF-8. Então se seu banco/sistema for ISO-8859-1 tem que converter a base de alguma forma.

    Responder

  • 02/06/2010
    11:33

    João Paulo escreveu:

    Responder

  • 02/06/2010
    11:34

    João Paulo escreveu:

    echo htmlentities($menu_sub['nome'], ENT_COMPAT, 'UTF-8')

    Responder

  • 25/01/2010
    11:22

    tiago escreveu:

    Estou utilizando outro sistema, só com javascript e mais nada. Quem quiser me pedir, é só adicionar no MSN tbrazil@hotmail.com

    Responder

  • 15/02/2010
    13:34

    Perroni escreveu:

    Mais um...

    Obrigado graças as pessoas como você que os sobrinhos evoluem.

    Responder

  • 06/03/2010
    08:09

    Roberto cezar escreveu:

    Cara valeu show de bola o escript valeu mesmo um abraço

    Responder

  • 04/04/2010
    15:50

    Rafael Gilead escreveu:

    Meu velho, conseguí por para funcionar perfeitamente. O problema que estou encontrando é para por a cidade para ser selecionada em uma página para EDITAR o cadastro.
    Pois eu coloco o estado SELECTED mas as cidades só aparecem se eu mecher no select dos estados.

    Responder

  • 14/04/2010
    11:34

    Ayesha escreveu:

    Olá, inclui o cod no site q estou desenvolvendo e nao carrega as cidade de maneira alguma... aqui esta o php em questao: http://www.canalcomercio.tv/tecnica/teste/cadastro/cadpj.php

    alguma ideia?

    Responder

  • 17/04/2010
    07:34

    Davi Ferreira escreveu:

    Seu servidor não tem suporte nativo a JSON. Utilize esta classe: http://mike.teczno.com/json.html.

    Responder

  • 16/04/2010
    10:45

    Plínio escreveu:

    Só não entendi o pq do campo id do estado ser um int(11) se temos somente 27 estados.

    Responder

  • 13/05/2010
    00:40

    Ulisses Costa escreveu:

    Amigo, desculpe a B.O, estou com a mesma dificulde em carregar as cidades no servidor remoto, já baixei a biblioteca sujerida mais nada, abaixo segue o o codigo.

    require("JSON.php"); //importando a biblioteca
    $json = new Services_JSON();//criando o objeto para manipular a JSON
    ...
    $output = $json->encode($cidades);
    echo($output);

    Responder

  • 13/05/2010
    13:50

    Marco Tulio escreveu:

    Boa tarde!
    Primeiramente muito obrigado pelo post.
    Estou a procurar por vários dias mas não encontrei nada que fizesse o que eu queria.
    Estou a ter o seguinte problema:
    Para inserir um combobox dá certo. Funciona ok!
    Porém quando coloco 2 combobox não está a funcionar.
    Não sei o que devo fazer. Você pode me dar alguma dica de como fazer isso?
    Obrigado!

    Responder

  • 15/05/2010
    18:29

    Davi Ferreira escreveu:

    Fala Marco,

    você vai precisar desenvolver uma função ou duplicar (ugh!) o trecho de código modificando os IDs dos elementos (#cod_cidades, #cod_estados).

    Responder

  • 19/05/2010
    13:22

    Sidarta escreveu:

    Olá
    Obrigado pelo código, mas estou com um problema, o primeiro e o último valor que da lista que é carregada estão como null. O que pode ser?

    Responder

  • 22/07/2010
    09:20

    Brunno Velasco escreveu:

    Pra resolver o problema da acentuação:

    mysql_query("SET NAMES 'utf8'", $con);
    mysql_query('SET character_set_connection=utf8', $con);
    mysql_query('SET character_set_client=utf8', $con);
    mysql_query('SET character_set_results=utf8', $con);

    Responder

  • 24/07/2010
    10:09

    Junior Eberhardt escreveu:

    No meu aqui apareceu certinho, quando seleciono algum estado, algumas cidades aparecem listadas como null...?

    Responder

  • 27/07/2010
    19:51

    Leonardo escreveu:

    Uma dúvida! Se eventualmente o meu select cod_estado já estiver um valor selecionado logo de cara... sem que eu tenha que selecionar... como fazer com que a página já saiba disso e escolha as cidades... e/ou A cidade também??

    Responder

Deixe seu comentário

Todos os campos são obrigatórios.

(Seu e-mail não será divulgado - serve apenas para validação e gravatar.)

(Tags HTML permitidas: <strong> <em> <code> <a>)

Opções

Ordenar por

Modo de exibição