Hoje vou compartilhar com vocês um serviço para o Symfony2 que criei para recuperar um endereço (logradouro, bairro, cidade e estado) apartir do CEP usando o web service dos Correios do Brasil. Isto pode ser de grande utilidade em formulários de cadastro.
No screenshot do formulário encima, o usuário ao entrar o CEP e clicar na Lupa os campos Logradouro, Bairro, Cidade e Estado vão se autocompletar.
Primeiro vamos criar o nosso service chamado CorreiosService. O método cep2endereco(string cep) vai chamar a seguinte URL http://m.correios.com.br/movel/buscaCepConfirma.do enviando o CEP em formato 99999-999 ou 99999999 por POST, vai parsear o resultado e retornar ele dentro de um Array.
Project/src/AppBundle/Service/CorreiosService.php
<?php namespace AppBundle\Service; class CorreiosService { // >=PHP5 public function cep2endereco($cep) { $url = 'http://m.correios.com.br/movel/buscaCepConfirma.do'; $data = array( 'cepEntrada' => $cep, 'metodo' => 'buscarCep' ); $options = array( 'http' => array( 'header' => "Content-type: application/x-www-form-urlencoded\r\n", 'method' => 'POST', 'content' => http_build_query($data), ), ); $context = stream_context_create($options); $html = file_get_contents($url, false, $context); $html = preg_replace("/\s+/", " ", $html); $pattern = "/<span class=\"respostadestaque\">([^<]*)<\/span>/"; preg_match_all($pattern, $html, $out); if (isset($out[1][0]) && isset($out[1][1]) && isset($out[1][2])) { $endereco = array( 'logradouro' => utf8_encode(preg_replace("/ - .*/", "", trim($out[1][0]))), 'bairro' => utf8_encode(trim($out[1][1])), 'cidade' => utf8_encode(split(" /", trim($out[1][2]))[0]), 'estado' => utf8_encode(split(" /", trim($out[1][2]))[1]) ); } else { $endereco = array( 'logradouro' => '', 'bairro' => '', 'cidade' => '', 'estado' => '' ); } return $endereco; } }
Uma vez que já temos nosso service vamos declarar ele no arquivo services.yml com o nome de app.correios para poder acessar ele em qualquer lugar dentro do nosso projeto.
Project/app/config/services.yml
services: app.correios: class: AppBundle\Service\CorreiosService
É possivel acessar o nosso serciço desde qualquer controller de esta maneira.
$endereco = $this->get('app.correios')->cep2endereco($cep);
Agora que temos acesso o nosso serviço, vamos criar um controller chamado AjaxController na rota (/ajax/cep/). Quando enviamos o CEP por GET (/ajax/cep/?cep=22020-002) ele retorna um JSON como o endereço com a estrutura seguinte.
{ "logradouro":"Avenida Nossa Senhora de Copacabana", "bairro":"Copacabana", "cidade":"Rio de Janeiro", "estado":"RJ" }
Project/src/AppBundle/Controller/AjaxController.php
<?php namespace AppBundle\Controller; use Symfony\Bundle\FrameworkBundle\Controller\Controller; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\JsonResponse; use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route; /** * @Route("/ajax") */ class AjaxController extends Controller { /** * @Route("/cep/", name="ajax_cep") */ public function cepAction(Request $request) { return new JsonResponse( $this->get('app.correios')->cep2endereco($request->get('cep')) ); } }
Agora vamos criar um script Javascript usando a librária jQuery para detectar o evento de click no botão de Lupa e ai como o método getJSON enviar o CEP colocado no primeiro input, recuperar o JSON retornado pelo controller e preencher os inputs logradouro, bairro, cidade e estado automaticamente.
Project/web/assets/js/scripts.js
$(document).ready(function() { $('.btn-cep2endereco').click(function() { var ico = $(this).children('i'); ico.attr('class', 'fa fa-circle-o-notch fa-spin'); $.getJSON($(this).data('route'), { cep: $('.ac-cep').val() }) .done(function(data) { if (data.logradouro != '') { $('.ac-logradouro').val(data.logradouro); $('.ac-bairro').val(data.bairro); $('.ac-cidade').val(data.cidade); $('.ac-estado').val(data.estado); ico.attr('class', 'fa fa-check'); } else { ico.attr('class', 'fa fa-ban'); } setTimeout(function(){ ico.attr('class', 'fa fa-search'); }, 2000); }) .fail(function() { ico.attr('class', 'fa fa-exclamation-circle'); setTimeout(function(){ ico.attr('class', 'fa fa-search'); }, 2000); }); }); });
Finalmente, já só falta a view da secção do form onde sai o template do endereço (não vou colocar o Form Type nem a Entity neste exemplo). Este exemplo usa Bootstrap3 e os iconos FontAwesome. O mais importante aqui é colocar as seguintes classes css para indicar a que campo pertence cada input (ac-cep, ac-logradouro, ac-bairro, ac-cidade e ac-estado), a classe btn-cep2endereco, e a propiedade data-route=”{{ path(‘ajax_cep’) }}” no botão da Lupa.
Project/app/Resources/views/form.html.twig
<div class="panel panel-default"> <div class="panel-heading"> <h3 class="panel-title"><i class="fa fa-home"></i> Endereço</h3> </div> <div class="panel-body"> <div class="row"> <div class="col-md-4"> <div class="form-group"> {{ form_label(form.cep) }} <div class="input-group"> {{ form_widget(form.cep, {'attr': { 'class': 'form-control ac-cep', 'placeholder': 'CEP', 'data-mask': '99999-999' }}) }} <span class="input-group-btn"> <button type="button" class="btn btn-default btn-cep2endereco tooltip-ajuda" data-toggle="tooltip" data-placement="left" title="Busca endereço a partir do CEP" data-route="{{ path('ajax_cep') }}"> <i class="glyphicon glyphicon-search"></i> </button> </span> </div> {{ form_errors(form.cep) }} </div> </div> <div class="col-md-8"> <div class="form-group"> {{ form_label(form.logradouro) }} {{ form_widget(form.logradouro, {'attr': { 'class': 'form-control ac-logradouro', 'placeholder': 'Logradouro' }}) }} {{ form_errors(form.logradouro) }} </div> </div> </div> <div class="row"> <div class="col-md-4"> <div class="form-group"> {{ form_label(form.bairro) }} {{ form_widget(form.bairro, {'attr': { 'class': 'form-control ac-bairro', 'placeholder': 'Bairro' }}) }} {{ form_errors(form.bairro) }} </div> </div> <div class="col-md-3"> <div class="form-group"> {{ form_label(form.cidade) }} {{ form_widget(form.cidade, {'attr': { 'class': 'form-control ac-cidade', 'placeholder': 'Cidade' }}) }} {{ form_errors(form.cidade) }} </div> </div> <div class="col-md-2"> <div class="form-group"> {{ form_label(form.estado) }} {{ form_widget(form.estado, {'attr': { 'class': 'form-control ac-estado', 'placeholder': 'Estado' }}) }} {{ form_errors(form.estado) }} </div> </div> <div class="col-md-3"> <div class="form-group"> {{ form_label(form.numero) }} {{ form_widget(form.numero, {'attr': { 'class': 'form-control', 'placeholder': 'Número' }}) }} {{ form_errors(form.numero) }} </div> </div> </div> </div> </div>
Cualquer duvida é so deixar um comentario.