Back-End

3 jan, 2011

Instant com PHP, XML e jQuery

Publicidade

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.

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.

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

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

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

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

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

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

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

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.

Recursos

Aprender

  • jQuery: Descubra mais no site, incluindo todos os plugins.
  • XML: Verifique a página de normas sobre XML para obter mais informações.
  • JSON.org: Visite esse ótimo local e descubra mais sobre como a JSON funciona e quais linguagens a suportam.
  • A
    página inicial de PHP: Visite um excelente recurso para desenvolvedores de PHP.
  • Mais artigos deste autor (Jack Herrington, developerWorks, março de 2005-atualmente): Leia artigos sobre Ajax, JSON, PHP, XML e outras tecnologias.
  • Área de XML do developerWorks: Obtenha os recursos necessários para melhorar suas qualificações na esfera de XML.
  • Certificação XML da IBM: Descubra como se tornar um Desenvolvedor Certificado pela IBM em XML e tecnologias relacionadas.
  • Biblioteca técnica de XML:
    Consulte a zona de XML para obter uma ampla gama de artigos técnicos e
    dicas, tutoriais, padrões e Redbooks da IBM. Leia também mais dicas de XML.
  • Podcasts do developerWorks: Ouça entrevistas e discussões interessantes para desenvolvedores de software.

Obter produtos e tecnologias

Discutir

***

artigo publicado originalmente no developerWorks Brasil, por Jack Herrington


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.