Instant com PHP, XML e jQuery

Aprenda como usar Ajax para construir um recurso "instantâneo" no seu aplicativo da Web.

Construa recursos de estilo "instantâneos" no seu Web site com uma combinação de jQuery, XML e PHP. É possível pegar e usar o código que encontrar neste artigo como desejar.

Jack D. Herrington, Senior Software Engineer, Leverage Software Inc.

Jack Herrington é engenheiro, autor e apresentador que mora e trabalha em Bay Area. É possível se manter atualizado em relação ao seu trabalho e aos seus escritos em http://jackherrington.com.



13/Dez/2010

Introdução ao Instant

Acrônimos usados frequentemente

  • Ajax: Asynchronous JavaScript + XML - JavaScript Assíncrono + XML
  • CSS: Cascading Stylesheets - Folhas de Estilo em Cascata
  • DOM: Document Object Model - Modelo de Objeto de Documento
  • HTML: Hypertext Markup Language - Linguagem de Marcação de Hipertexto
  • JSON: JavaScript Object Notation - Notação de Objeto JavaScript
  • UI: User Interface - Interface com o usuário
  • URL: Uniform Resource Locator - Localizador Uniforme de Recursos
  • XML: Extensible Markup Language - Linguagem de Marcação Extensível

O recurso Instant do Google, um novo aprimoramento de procura que mostra resultados enquanto você digita, tem causado muito burburinho, e é fácil entender por quê. Tudo o que você precisa fazer para começar a obter resultados é digitar. Não é preciso pressionar a tecla ENTER para ver os resultados, depois ajustar sua procura e pressionar ENTER novamente. Tudo acontece enquanto você digita. Se você ainda não experimentou, experimente. É incrível como uma mudança tão pequena pode fazer uma diferença tão grande na usabilidade.

O incrível sobre esse tipo de funcionalidade Instant é que ela é fácil de implementar, especialmente quando você usa boas ferramentas do lado do cliente como jQuery (consulte Recursos ). Neste artigo, você seguirá o processo de construção de um mecanismo de procura simples e depois construirá uma interface com o usuário de procura instantânea para esse mecanismo.

Tudo começa com obter os dados para procura.


Configuração dos dados

Para este artigo, decidi procurar episódios dos Simpsons. Coloquei um arquivo XML (incluso no download) de origem que contém todos os episódios dos Simpsons, seus títulos, temporada, número do episódio, data de transmissão e um resumo do episódio. É possível ver uma parte do XML na listagem 1.

Listagem 1. A origem de dados XML
<?xml version="1.0" encoding="UTF-8"?>
<episodes>
  <episode title='Simpsons Roasting on an Open Fire' episode='1' season='1' 
  aired='17 December 1989'>
     Christmas seems doomed for the Simpson family when Homer receives no
    Christmas Bonus. Homer becomes a mall Santa Claus, hoping to make money and
    bring Marge, Bart, Lisa, and baby Maggie, a happy holiday.
  </episode>
   ...
</episodes>

Trata-se de um arquivo realmente grande, pesando cerca de 840 K. O que não deveria ser nenhuma surpresa, já que os Simpsons tiveram uma temporada longa de 22 anos.

A próxima coisa a se fazer é escrever uma classe de PHP que analise o XML e faça a procura por você. Essa classe é chamada de Simpsons e está na listagem 2.

Listagem 2. A classe de procura Simpsons
<?php
class Simpsons {
 private $episodes = array();
 public function __construct() {
   $xmlDoc = new DOMDocument();
   $xmlDoc->load("simpsons.xml");
 foreach ($xmlDoc->documentElement->childNodes as $episode)
   {
     if ( $episode->nodeType == 1 ) {
      $this->episodes []= array( 
      'episode' => $episode->getAttribute( 'episode' ),
      'season' => $episode->getAttribute( 'season' ),
      'title' => $episode->getAttribute( 'title' ),
      'aired' => $episode->getAttribute( 'aired' ),
      'summary' => $episode->nodeValue );
     }
   }
 }
 public function find( $q ) {
   $found = array();
   $re = "/".$q."/i";
   foreach( $this->episodes as $episode ) {
     if ( preg_match( $re, $episode['summary'] ) || 
        preg_match( $re, $episode['title'] ) ) {
     $found []= $episode;
   }
   }
   return $found;
 }
}
?>

O construtor da classe lê o arquivo XML com as informações do episódio usando a biblioteca DOM de XML que é padrão para PHP. Ele itera por todos os filhos do nó-raiz, extraindo os atributos de temporada, título, data de transmissão e episódio e o texto do nó que contém o resumo. Ele então anexa todos esses dados como hashtable para a array de episódios, que é uma variável de membro.

A função find procura a lista de episódios para encontrar correspondências usando uma correspondência simples de expressão regular no título e no resumo. Quaisquer episódios que tenham correspondência são anexados à array, que é então retornado para o responsável pela chamada. Se a array estiver vazia é porque nenhuma correspondência foi encontrada.

Com os dados em mãos, a próxima etapa é começar a construir o respondente do Ajax que o seu Instant UI chama para recuperar os dados.


Criação da página de resposta do Ajax

A primeira versão da UI usa uma resposta HTML para a solicitação Ajax. Essa abordagem é a forma mais fácil de implementar o Instant UI. A página da Web do Instant UIpega o termo de procura e faz uma solicitação Ajax ao servidor usando esse termo. O servidor formata então um bloco de HTML que faz a resposta e envia de volta para a página. O código na página da Web do Instant UI substitui, então, uma parte da página pelo HTML atualizado em uma única chamada simples.

Mais adiante neste artigo, demonstrarei como usar uma resposta XML e uma resposta JSON do servidor, mas, no momento, só para tornar as coisas mais fáceis, começaremos com a versão HTML.

A primeira coisa de que você precisa é a página de resposta HTML. Essa página pega uma cadeia de consultas da solicitação. Depois, usa essa cadeia para chamar a classe Simpsons para procurar os episódios. Finalmente formata a array retornada do episódio como HTML. O código para isso está na listagem 3.

Listagem 3. A página de resposta do AJAX em HTML
<?php
include 'Simpsons.php';

$s = new Simpsons();
$episodes = $s->find( $_REQUEST['q'] );
if ( count( $episodes ) == 0 ) {
?>
No results found
<?php	
} else {
?>
<table>
<?php foreach( $episodes as $e ) { ?>
<tr><td class="episode"><b><?php echo( $e['title'] ) 
?></b> - 
 Season <?php echo( $e['season'] ) ?> 
 Episode <?php echo( $e['episode'] ) ?> - 
 Aired on <?php echo( $e['aired'] ) ?></td></tr>
<tr><td class="summary"><?php echo( $e['summary'] ) 
?></td></tr>
<?php } ?>
</table>
<?php
}
?>

Na parte superior, a listagem 3 inclui a classe Simpsons. O código cria, então, uma nova instância dela e faz a chamada de localização. Depois ele olha para ver se a resposta está vazia e se ela retorna “No Results Found” (Nenhum resultado encontrado); do contrário, ele faz o loop através dos resultados e forma uma tabela de resultados.

Para testar a página, basta ir até o seu navegador da Web e solicitar a página. É possível ver a saída na figura 1.

Figura 1. A página de resposta do Ajax em HTML
Screen capture of the HTML Ajax response page

Nesse ponto, você tem tudo de que precisa para começar a construir o Instant Search UI.


Construção do Instant Search UI

Usar a biblioteca jQuery de JavaScript torna a construção do Instant Search UI incrivelmente fácil. Consulte a listagem 4 e você verá o que estou dizendo.

Listagem 4. A página do Instant usando respostas HTML
<html><head>
<script src="jquery-1.4.2.min.js"></script>
<link rel="stylesheet" href="styles.css" type="text/css" />
<title>Instant Search - HTML Based</title>
</head>
<body>
Simpsons Search: <input type="text" id="term" />
<div id="results">
</div>
<script>
$(document).ready(function() {
$('#term').keyup(function() {
  $.get('search_html.php?q='+escape($('#term').val()), function(data) {
    $('#results').html(data);
  } );
} );
} );
</script>
</body>
</html>

Na parte superior da página, a listagem 4 inclui a biblioteca jQuery e uma folha de estilo CSS para tornar a saída mais bonita. O corpo da página inclui um campo de entrada para o termo de procura e um div de resultados que contém a saída.

A maior parte do trabalho é realizada na seção JavaScript na parte superior da página. Tudo começa com uma chamada para a função ready no documento. Essa chamada assegura que o Javascript interior não seja executado até que a página esteja pronta. O Javascript de dentro usa a função keyup no objeto de entrada do termo de procura para monitorar o pressionamento de tecla no campo de termo de procura. Quando o campo de texto muda, o método get do Ajax é chamado para o servidor. E a resposta de dados dessa chamada é usada para preencher o elemento de resultados usando o html .

Se o código JavaScript parecer um ruído de linha, está tudo bem. Esse é, na verdade, o nível tecnológico do JavaScript, já que quanto menor o tamanho do código melhor ele é mantido, porque o código precisa verificar a ligação.

É possível fazer todo esse trabalho sem a biblioteca jQuery, mas a vantagem de se usar a biblioteca é que o código é conciso e todo o trabalho de plataforma cruzada já foi feito para você. Você não precisa se preocupar com o Internet Explorer® em relação ao Safari ou ao Firefox; você escreve o código uma vez e ele funciona em qualquer lugar.

Para testar a biblioteca, vá até a página do Instant Search UI no navegador da Web. É possível ver algo como na Figura 2.

Figura 2. Poucas letras digitadas no termo de procura
Screen capture of search with only a few letters typed in the search term

A figura 2 mostra a interface depois que alguns caracteres foram inseridos. Depois que termino de digitar o termo "frink", você vê os resultados na Figura 3.

Figura 3. Depois de concluir o termo
Screen capture of results after the term is completely typed

A figura 3 mostra “frink” aparecendo no título ou sinopse de dois episódios. Dados idiotas! O professor Frink (de longe o melhor personagem da série) participou de muito mais do que apenas dois episódios. Mas, ainda assim, isso é uma coisa muito perfeita. O tempo de resposta na minha máquina local foi excelente, mesmo com o código do servidor analisando 840 K de XML.

Agora você pode querer regular o número de solicitações, colocando um atraso entre cada pressionamento de tecla e quando você realmente faz a solicitação. O código atualizado para se fazer isso está na listagem 5.

Listagem 5. A página do Instant usando respostas HTML com um atraso
<html><head>
<link rel="stylesheet" href="styles.css" type="text/css">
<script src="jquery-1.4.2.min.js"></script>
<title>Instant Search - HTML Based With Delay</title>
</head>
<body>
Simpsons Search: <input type="text" id="term" />
<div id="results">
</div>
<script>
delayTimer = null;

function getResults() {
  $.get('search_html.php?q='+escape($('#term').val()), function(data) {
    $('#results').html(data);
  } );
  delayTimer = null;
}

$(document).ready(function() {
$('#term').keyup(function() {
  if ( delayTimer )
     window.clearTimeout( delayTimer );
  delayTimer = window.setTimeout( getResults, 200 );
} );
} );
</script>
</body>
</html>

Esse código cria um cronômetro quando o usuário pressiona uma tecla. Depois que se passarem 200 milissegundos no cronômetro, a solicitação será feita. Se outro pressionamento de tecla vier antes do cronômetro parar, o cronômetro original é cancelado e um novo cronômetro é criado. O resultado é que o cronômetro expira 200 milissegundos depois que o usuário parar de digitar. A interface ainda parece tão veloz quanto a original, mas o número de solicitações feitas ao servidor é substancialmente reduzido, particularmente quando usuários digitam rapidamente.

Poderíamos parar por aqui, mas há, na verdade, mais duas formas de se fazer esse processo instantâneo.


Migrar para XML

A primeira forma é usar XML como sintaxe de transporte do servidor para o cliente. A ideia aqui é a de que o servidor forneça um terminal XML genérico que qualquer processo possa usar para fazer consultas e que o seu cliente seja inteligente o suficiente para ler o XML e formatá-lo da forma que desejar.

Para mudar para XML, primeiro crie uma nova página do servidor como na listagem 6.

Listagem 6. A página do Ajax em XML
<?php
include 'Simpsons.php';

header( 'Content-type: text/xml' );

$s = new Simpsons();
$doc = new DOMDocument();
$root = $doc->createElement( 'episodes' );
$doc->appendChild( $root );
foreach( $s->find( $_REQUEST['q'] ) as $episode ) {
   $el = $doc->createElement( 'episode' );
   $el->setAttribute( 'title', $episode['title'] );
   $el->setAttribute( 'episode', $episode['episode'] );
   $el->setAttribute( 'season', $episode['season'] );
   $el->setAttribute( 'aired', $episode['aired'] );

   $tn = $doc->createTextNode( $episode['summary'] );
   $el->appendChild( $tn );

   $root->appendChild( $el );
}
print $doc->saveXML();
?>

A procura continua exatamente a mesma, o que muda é como você formata os resultados. Agora o código cria um documento XML e anexa nós a ele que contêm todos os dados retornados. Depois, no final do script, ele simplesmente salva o DOM de XML como cadeia de caractere. Observe que na parte superior do script, você também define o tipo de conteúdo como texto/xml quando exporta XML e não HTML.

Se você navegar até essa página no seu navegador da Web, verá algo parecido com a figura 4.

Figura 4. A página de resposta em XML
Screen capture of the XML response page

Alguns navegadores, no entanto, devem mostrar o texto retornado de uma maneira um pouco mais estruturada. Se quiser ver o XML de origem original, é possível escolher View - Source (Visualizar - Origem) para ver algo parecido com a janela na figura 5.

Figura 5. A origem da página de resposta em XML
Screen capture of the XML response page source

Como você pode ver, o script criou alguns XML bem formatados, prontos para um novo pedaço de código do lado do cliente.

O novo código do lado do cliente que analisa XML em vez de usar o HTML diretamente está na listagem 7.

Listagem 7. A página do Instant Search usando XML
<html><head>
<script src="jquery-1.4.2.min.js"></script>
<link rel="stylesheet" href="styles.css" type="text/css" />
<title>Instant Search - XML Based</title>
</head>
<body>
Simpsons Search: <input type="text" id="term" />
<table id="results">
</table>
<script>
$(document).ready( function() {
$('#term').keyup( function() {
 $.get('search_xml.php?q='+escape($('#term').val()), function(data) {
html = '<table id="results">';
$(data).find('episode').each( function() {
     var ep = $(this);
         html += '<tr><td class="episode"><b>'+
         ep.attr('title')+'</b>&nbsp;';
         html += 'Season '+ep.attr('season')+'&nbsp;';
         html += 'Episode '+ep.attr('episode')+'&nbsp;';
         html += 'Aired '+ep.attr('aired')+'</td></tr>';
         html += '<tr><td class="summary">'+
         ep.text()+'</td></tr>';
   } );
   html += '</html>';
   $('#results').replaceWith( html );
 } );
} );
} );
</script>
</body>
</html>

O código do cliente para monitorar os pressionamentos de tecla e para fazer solicitação Ajax é quase exatamente o mesmo. A diferença é a URL diferente para pegar os dados XML em vez dos dados HTML.

Depois que os dados retornam, o código usa jQuery para localizar todas as identificações de episódio. Ele formata então uma grande parte do XML e usa a função replaceWith para substituir a tabela antiga pela nova. Por causa da jQuery, esse código é incrivelmente mais fácil de se usar do que seria com as funções nativas de DOM do navegador.

Outra maneira de transferir os dados é através de JSON (JavaScript Object Notation).


Migrar para JSON

JSON é uma forma muito popular de mover dados pelo mundo da Web 2.0. Ela é compacta, fácil e rápida para o navegador ler, porque tudo o que ela precisa fazer é avaliar o código JavaScript retornado. Além disso, é muito fácil criar JSON, como você poderá ver na versão JSON da página de procura do Ajax na listagem 8.

Listagem 8. A página do Ajax em JSON
<?php
include 'Simpsons.php';

header( 'Content-type: application/json' );

$s = new Simpsons();
print json_encode( $s->find( $_REQUEST['q'] ) );
?>

Você só precisa usar a função json_encode para transformar a array retornada em um código JSON. Se você é curioso, também há uma função json_decode que pode transformar a JSON de volta para tipos básicos de PHP. A maioria das linguagens populares possui mecanismos JSON que são tão fáceis como este para converter estrutura básica de dados em e de JSON.

Se você olhar a página JSON no navegador, você verá algo como a página de resposta na figura 6.

Figura 6. A página de resposta em JSON
Screen capture of the JSON response page

Esta página pode não ser muito atraente para o olho humano, mas para o interpretador de JavaScript no navegador ela parece um paraíso de leitura fácil.

O código da página da Web correspondente do Instant UI para ler a saída formatada em JSON está na listagem 9.

Listagem 9. O Instant Search UI em JSON
<html><head>
<script src="jquery-1.4.2.min.js"></script>
<link rel="stylesheet" href="styles.css" type="text/css" />
<title>Instant Search - JSON Based</title>
</head>
<body>
Simpsons Search: <input type="text" id="term" />
<table id="results">
</table>
<script>
$(document).ready( function() {
$('#term').keyup( function() {
 $.get('search_json.php?q='+escape($('#term').val()), function(data) {
   html = '<table id="results">';
   $.each( data, function( ind, ep ) {
         html += '<tr><td class="episode"><b>'+ep.title+'</b>&nbsp;';s
         html += 'Season '+ep.season+'&nbsp;';
         html += 'Episode '+ep.episode+'&nbsp;';
         html += 'Aired '+ep.aired+'</td></tr>';
         html += '<tr><td class="summary">'+ep.summary+'</td></tr>';
   } );
   html += '</html>';
   $('#results').replaceWith( html );
 } );
} );
} );
</script>
</body>
</html>

Esse código é muito parecido com o código XML, com a diferença de que é possível usar a função each da jQuery na array retornada, depois usar a notação de ponto para acessar todas as chaves importantes nos dados (ou seja, título, episódio, resumo, e assim por diante.

Aí está: uma implementação rudimentar o recurso de procura do Instant que você pode usar como ponto de partida para o seu próprio trabalho.


Só mais algumas coisas

Essa implementação possui três diferenças principais em relação àquilo que os desenvolvedores do Google fizeram. A primeira é a escala. Eles já estavam manipulando bilhões de procuras por dia, agora eles estão manipulando bilhões de pequenas procuras individuais com cada pressionamento de tecla. Há muitas questões e soluções em torno disso, mas, neste caso, você tem pelo menos uma coisa trabalhando a seu favor—o cache do navegador. Se o usuário digita o mesmo termo duas vezes, devido ao cache do navegador somente uma solicitação é realmente feita, porque na segunda vez é solicitado que o navegador retorne os dados em cache.

Outra coisa que o Google faz é fazer uma pré-busca dos resultados. Por exemplo, se você digitar “mov”, ele presume que você está procurando por “movies” (filmes), faz a procura e indica com texto na cor cinza o “ies” que está faltando.

A terceira diferença é o suporte para paginação, que é muito fácil de resolver. Tudo o que você precisa fazer é adicionar JavaScript para links de páginas na parte inferior da página e depois chamar esse script quando o usuário clicar para navegar da primeira página para qualquer página subsequente.


Conclusão

O recurso Instant UI do Google é realmente instantâneo. Ele é revolucionário? Na verdade, não. Mas é uma pequena etapa que possui implicações profundas para a usabilidade. Como é possível ver a partir deste artigo, os princípios do comportamento não são difíceis de implementar usando ferramentas padrão como XML, PHP e jQuery.

Espero que você esteja apto a usar o código apresentado aqui em seus próprios projetos. Se fizer isso, por favor, me avise. Adoraria dar uma olhada em como você o usou.


Download

DescriçãoNomeTamanho
Source code for articlesrc.zip82KB

Recursos

Aprender

Obter produtos e tecnologias

Discutir

Comentários

developerWorks: Conecte-se

Los campos obligatorios están marcados con un asterisco (*).


Precisa de um ID IBM?
Esqueceu seu ID IBM?


Esqueceu sua senha?
Alterar sua senha

Ao clicar em Enviar, você concorda com os termos e condições do developerWorks.

 


A primeira vez que você entrar no developerWorks, um perfil é criado para você. Informações no seu perfil (seu nome, país / região, e nome da empresa) é apresentado ao público e vai acompanhar qualquer conteúdo que você postar, a menos que você opte por esconder o nome da empresa. Você pode atualizar sua conta IBM a qualquer momento.

Todas as informações enviadas são seguras.

Elija su nombre para mostrar



Ao se conectar ao developerWorks pela primeira vez, é criado um perfil para você e é necessário selecionar um nome de exibição. O nome de exibição acompanhará o conteúdo que você postar no developerWorks.

Escolha um nome de exibição de 3 - 31 caracteres. Seu nome de exibição deve ser exclusivo na comunidade do developerWorks e não deve ser o seu endereço de email por motivo de privacidade.

Los campos obligatorios están marcados con un asterisco (*).

(Escolha um nome de exibição de 3 - 31 caracteres.)

Ao clicar em Enviar, você concorda com os termos e condições do developerWorks.

 


Todas as informações enviadas são seguras.


static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=80
Zone=Software livre, Desenvolvimento da Web
ArticleID=600704
ArticleTitle=Instant com PHP, XML e jQuery
publish-date=12132010