Construindo uma select box com options condicionais utilizando cakephp e ajax
Ao acompanhar a lista de discussão CakePHP Tuga percebi que existe uma dúvida que sempre dá as caras por lá: preencher as opções de um select box baseado na escolha de um select box preenchido anteriormente. O maior exemplo disso é o famoso “select cidade estado” ou “combo box cidade estado”.
Essa dúvida retornou novamente, mas através de um e-mail enviado para o pessoal da empresa por um amigo de faculdade com a mesma dúvida. No e-mail ele ainda referenciava duas fontes [1] [2] em que havia tentado, mas não obtia sucesso. Talvez a dificuldade seja o idioma, então decidi fazer um exemplo e disponibilizar para a comunidade. Este exemplo utiliza a famosa combinação de seleção do estado e depois da cidade, sendo que o select box de cidades é preenchido a partir da escolha do estado.
Criei mais coisas que o necessário pensando que esses arquivos poderão ser utilizados para outros projetos, um exemplo disso é o modelo de cidades, que não possui basicamente nada, mas pode ser aumentado para inserção de cidades em um determinado estado.
Atualmente tudo deve ser feito diretamente no banco de dados. Vamos iniciar com sua criação:
DROP TABLE IF EXISTS `cidades`; CREATE TABLE IF NOT EXISTS `cidades` ( `id` int(10) UNSIGNED NOT NULL AUTO_INCREMENT, `estado_id` int(10) UNSIGNED NOT NULL, `cidade` varchar(50) collate utf8_unicode_ci NOT NULL, PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8 ; DROP TABLE IF EXISTS `estados`; CREATE TABLE IF NOT EXISTS `estados` ( `id` int(10) UNSIGNED NOT NULL AUTO_INCREMENT, `sigla` char(2) character SET utf8 NOT NULL, `estado` varchar(30) character SET utf8 NOT NULL, PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8 ;
Logo depois criamos os modelos:
class Estado extends AppModel { var $name = 'Estado'; var $hasMany = array( 'Cidade' => array( 'dependent' => true ) ); } class Cidade extends AppModel { var $name = 'Cidade'; var $belongsTo = 'Estado'; }
Até esse ponto nenhuma novidade, agora vamos aos controllers. O controller de cidades está quase vazio, contém somente sua declaração.
Vou colocar apenas o de estados.
class EstadosController extends AppController { var $name = 'Estados'; var $helpers = array('Html', 'Form', 'Javascript', 'Ajax'); var $components = array('RequestHandler'); function lista_cidades() { $estados = $this->Estado->find('list', array( 'fields' => array('Estado.id', 'Estado.estado'), ) ); $this->set('estados', $estados); } function cidades_estado() { $cidades = $this->Estado->Cidade->find('list', array( 'conditions' => array('Cidade.estado_id' => $this->data['Estado']['estado']), 'fields' => array('Cidade.id', 'Cidade.cidade'), 'order' => array('Cidade.cidade ASC') ) ); $this->set('cidades', $cidades); } }
Aqui começa a ficar interessante, observem que foi necessário carregar os helpers Ajax e Javascript além do component RequestHandler. Esses são os responsáveis pela mágica. O primeiro método é a página que possui as combo box de estado e cidade. O segundo, é responsável por trazer as cidades referentes a um estado.
Vejam agora a primeira view:
<?php echo $javascript->link('prototype', false); echo $javascript->link('http://cidades-estados-js.googlecode.com/files/cidades-estados-1.0.js', false); echo $javascript->link('cidade_estado', false); ?> <fieldset> <legend>CakePHP Way</legend> <?php echo $form->input('Estado.estado', array('options' => $estados)); echo $form->input('Estado.cidades', array('options' => array())); echo $ajax->observeField('EstadoEstado', array( 'update' => 'EstadoCidades', 'url' => array('controller' => 'estados', 'action' => 'cidades_estado') ) ); ?> </fieldset> <fieldset> <legend>Cidades-Estado</legend> <?php echo $form->input('estado', array('type' => 'select')); echo $form->input('cidade', array('type' => 'select')); ?> </fieldset>
Nesta view chamados o prototype, necessário para que o cake consiga realizar as requisições ajax, preencher o segundo select box, etc. Lembrando que ele deverá estar na pasta webroot/js da sua aplicação. No primeiro fieldset declaramos dois campos select, sendo um já preenchido com os estados encontrados no banco e trazidos pelo controller, o segundo deverá ter seus valores preenchidos com as cidades de um estado selecionado. O método observeField do helper ajax é utilizado no select com id EstadoEstado, que chama a action cidades_estado do controller estado. Essa action é responsável por trazer a lista de cidades do estado selecionado. Ao finalizar a requisição, o select box com id EstadosCidade deverá ser atualizado com o resultado retornado pela action.
Esse resultado deverá ser um conjunto de options com as cidades, gerado na view abaixo:
if(!empty($cidades)) { foreach ($cidades as $id => $cidade) { echo '<option value="'.$id.'">'.$cidade.'</option>'; } }
Pronto, já temos um select de estados > cidade condicional em ajax funcionando. Lembrando que esse exemplo pode ser abstraído para qualquer outra necessidade semelhante.
Aproveitando que esse tipo de necessidade é bastate comum, aproveitei para mostrar um pequeno script que já faz todo o trabalho para você.
Trata-se de um projeto hospedado no googlecode, que faz todo o trabalho sujo para você chamado cidades-estado-js. A única coisa necessária é adicionar o fonte e dizer onde ele deverá funcionar.
Neste exemplo ele é o segundo script adicionado. O terceiro script é quem diz onde deverão ser colocadas as cidades e estados. Veja seu fonte, que utiliza prototype.
$(document).observe('dom:loaded', function(){ new dgCidadesEstados({ estado: $('estado'), cidade: $('cidade') }); });
Pronto, com poucas linhas de código já se tem toda a funcionalidade. Agora basta escolher a versão em CakePHP ou a cidades-estados-js.
Para quem deseja muita agilidade, utilize a cidades-estado-js. Para quem quer velocidade e flexibilidade total, mãos a obra com CakePHP.
Acredito que esse post do pinceladas web também possa interessar a quem está lendo até esse ponto. Ele contém um arquivo sql com todas as cidades e estados do Brasil.
[1] – http://www.devmoz.com/blog/2007/04/04/cakephp-update-a-select-box-using-ajax/
[2] – http://www.jamesfairhurst.co.uk/posts/view/using_ajax_to_populate_a_select_box_in_cakephp/




