 | Utilizando o Manipulador de Solicitação
O CakePHP vem com um Manipulador de Solicitação que lhe permite apresentar seu aplicativo de formas diferentes, dependendo do tipo de solicitação feita. Nas seções anteriores, você viu como os layouts controlam o wrapper do seu aplicativo, que, por padrão, no CakePHP é o XHTML Transitional. Aqui, examinaremos como você pode usar o Manipulador de Solicitação para retornar dados do seu aplicativo em uma forma diferente do XHTML, dependendo do que está solicitando a página.
Anatomia de uma solicitação HTTP
Cada elemento de uma página Web é obtido via solicitação HTTP, um cabeçalho de texto que contém o recurso sendo solicitado, e informações sobre o agente que solicita. Abaixo, uma amostra de solicitação HTTP.
Listagem 11. Amostra de solicitação HTTP
GET / HTTP/1.1
Host: www.google.com
User-Agent: Mozilla/5.0 (Macintosh; U; PPC Mac OS X Mach-O; en-US; rv:1.8.1)
Gecko/20061010 Firefox/2.0
Accept: text/xml,application/xhtml+xml,text/html
Accept-Language: en-us,en
Accept-Encoding: gzip,deflate
Accept-Charset: ISO-8859-1,utf-8
Keep-Alive: 300
Connection: keep-alive
Cache-Control: max-age=0
|
Em Web sites antigos, não era incomum tentar analisar a cadeia de caractere User-Agent, fornecendo conteúdo completamente diferente a navegadores Web diferentes. Por exemplo, uma empresa que projetasse seu site para Internet Explorer® podia descobrir que ele não aparecia corretamente em outros navegadores e teria de sugerir que seus visitantes usassem o IE.
Felizmente, esses dias estão chegando ao fim, e boas maneiras na Web significam que todo navegador Web deveria obter exatamente o mesmo conteúdo para exatamente os mesmos recursos. Note que o mesmo conteúdo não significa necessariamente que tenha de ser apresentado da mesma maneira.
Em vez de usar o User-Agent para determinar como formatar o conteúdo, um método melhor é usar o cabeçalho Accept. No exemplo acima, o Firefox aceita três tipos de conteúdo identificado por seus tipos de MIME: XML, XHTML+XML e HTML.
Criando um feed de RSS
Agora você criará um feed de RSS para o Tor, que é uma lista atualizada de todos os novos produtos. Isso mudou drasticamente desde o CakePHP V1.1.8: Agora, é mais fácil ainda. Você precisa incluir o componente do Manipulador de Solicitação no manipulador de Produtos, criar uma visualização que use o Auxiliar de RSS e dizer roteador para analisar as extensões RSS.
Dar esse comportamento à lista de produtos é fácil quando se usa o componente do Manipulador de Solicitação. Visto que o seu feed se localizará em /products, abra o arquivo que contém a classe ProductsController. Para começar, inclua o componente do Manipulador de Solicitação.
Listagem 12. Incluindo o componente do Manipulador de Solicitação
<?php
class ProductsController extends AppController
{
var $name = 'Products';
var $helpers = array('Html', 'Form' );
var $components = array('Acl', 'Security', 'RequestHandler');
...
}
|
Há muito mais coisas sobre o Manipulador de Solicitação do que aquilo que vamos abranger aqui. Você pode usá-lo para determinar que tipos de solicitações são aceitos, ou preferidos, e muito mais. Mas por agora, simplesmente incluí-lo será suficiente.
Depois, você precisar criar uma visualização apenas para o feed de RSS. Ela será renderizada automaticamente quando solicitado e usará o Auxiliar de RSS para realizar o trabalho necessário. Crie o arquivo app/views/products/rss/index.ctp (pode ser necessário criar esse diretório RSS). A aparência da visualização deve ser semelhante ao que se vê abaixo.
Listagem 13. Visualização RSS de índice de produtos
<?php
echo $rss->items($products, 'transformRSS');
function transformRSS($products) {
return array(
'title' => $products['Product']['title'],
'link' => array('url'=>'/products/view/'
.$products['Product']['id']),
'description' => $products['Product']['description'],
);
}
?>
|
Por fim, acrescente a seguinte linha ao arquivo app/config/routes.php: Router::parseExtensions('rss');.
é isso! Você pode ver o novo feed de RSS em http://localhost/products/index.rss. Talvez seja necessário ver a fonte para ler o XML. E se o seu nível de depuração for configurado para 2 ou 3, a informação de depuração sem dúvida invalidará o formato XML. Mas isso deve encaminhá-lo muito bem para brincar com RSS no Cake.
Acrescentando o Ajax
O Asynchronous JavaScript and XML (Ajax) é um método popular para criar aplicativos Web interativos sem sacrificar a compatibilidade do navegador. Nesta seção, descrevemos como usar o auxiliar do Ajax do CakePHP e os componentes do Manipulador de Solicitação para agilizar a atualização dos produtos. Presume-se que você tem uma familiaridade geral com os conceitos do Ajax. Se precisar de uma introdução, experimente ler "Dominando o Ajax" e "Avaliando o Ajax" (veja os Recursos).
Lembre-se de que o Ajax é uma série de solicitações assíncronas do cliente para o servidor, em geral iniciadas por uma interação do usuário. Visto que o CakePHP separa os plug-ins back-end e front-end em componentes e auxiliares, respectivamente, isso significa que todas as suas funções JavaScript serão implementadas pelo auxiliar do Ajax, enquanto toda a funcionalidade back-end será suportada pelo componente do Manipulador de Solicitação.
O componente do Manipulador de Solicitação permite de forma eficaz que você reutilize o código não-Ajax que criou. Ele tem um método isajax, que retorna verdadeiro se a solicitação tiver sido feita por uma chamada XMLHttpRequest. Dentro do seu controlador, você pode usar esse método para verificar a natureza da solicitação e ajustar um pouco a saída, dependendo de se o Ajax está envolvido.
script.aculo.us
O auxiliar do Ajax do CakePHP exige Protótipo para o Ajax e usa o script.aculo.us para os efeitos. Ambos são liberados por licença do MIT. (A licença do MIT é a mesma do CakePHP e basicamente diz que você pode usar o software por qualquer meio, com poucas restrições). A biblioteca script.aculo.us inclui uma cópia do Protótipo, então, não é necessário fazer o download de ambos. Comece fazendo o download da cópia mais recente do script.aculo.us (veja os Recursos). Na verdade, você não precisa conhecer muito o script.aculo.us, exceto para incluir seus arquivos de JavaScript no seu site.
Depois de fazer o download e extrair o script.aculo.us, você deve ver uma pasta chamada src com vários arquivos de JavaScript nela. Você não vai usar todos eles, mas pode copiar todos para a pasta app/webroot/js do seu site. Copie também o arquivo lib/prototype.js para app/webroot/js.
Agora que você tem o script.aculo.us, o JavaScript precisa ser incluído em cada página. Abra o arquivo app/views/layouts/default.ctp e acrescente as linhas relacionadas ao JavaScript mostradas abaixo.
Listagem 14. Alterando o cabeçalho
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<?php
if ( isset($javascript) ) {
echo $javascript->link('prototype.js');
echo $javascript->link('scriptaculous.js?load=effects');
echo $javascript->link('controls.js');
}
?>
...
|
Isso é basicamente tudo que você vai ter que fazer com a biblioteca script.aculo.us. Daqui por diante, tudo envolve apenas objetos no CakePHP.
Acrescentando uma lista de favoritos
Lembre-se da Parte 2 que você conseguiu fazer um link com dois modelos com os relacionamentos hasMany e belongsTo. Em especial, você criou um link entre Vendedores e Produto. Há outro relacionamento, hasAndBelongsToMany, que é especialmente útil ao fazer links com muitos modelos.
Para ilustrar, imagine que você criou os itens favoritos do usuário utilizando o relacionamento hasMany. Então, cada produto deve "pertencerA" determinado usuário, o que significa que nenhum outro usuário pode marcá-lo como favorito. Isso não seria bom. A estrutura de banco de dados resultante é mostrada na Figura 2.
Figura 2. Esquema de banco de dados para o relacionamento hasMany e belongsTo 
Note que um produto pode pertencer a apenas um usuário. Em vez disso, o relacionamento hasAndBelongsToMany usa uma "tabela de junção", que é um modo de assegurar que os produtos hasMany de um usuário, sem que os produtos precisem "pertencerA" um único usuário. A tabela de junção simplesmente rastreia uma lista de pares — um usuário e um produto — para indicar que os dois estão ligados. A estrutura de banco de dados resultante é mostrada abaixo.
Figura 3. Esquema de banco de dados para o relacionamento hasAndBelongsToMany 
Qualquer número de conexões pode ser feito entre as duas tabelas. Se você configurou uma tabela de junção adequadamente, o CakePHP vai criar automaticamente os links apropriados para você. Para assegurar que o CakePHP possa encontrar sua tabela de junção, há diversas convenções que você deve seguir:
- O nome da tabela deve incluir os nomes dos dois modelos, no plural e em ordem alfabética. Por exemplo, se você ligar um objeto de usuário e de grupo, o nome da tabela seria groups_users.
- A tabela deveria ter duas teclas estrangeiras, cada qual com o mesmo nome do relacionamento
belongsTo. Por exemplo, uma tabela de junção users/groups teria os campos user_id e group_id. - Para otimização, as duas chaves formariam uma chave primária na tabela de junção.
No nosso caso, estamos ligando produtos a usuários, de modo que a consulta do SQL vai criar uma tabela que atenda a essas normas.
Listagem 15. Crie uma tabela para ligar os produtos aos usuários
CREATE TABLE products_users (
product_id int(5) NOT NULL,
user_id int(5) NOT NULL,
PRIMARY KEY (product_id,user_id)
) ENGINE=MyISAM;
|
Para ligar os dois modelos, simplesmente acrescentamos o relacionamento hasAndBelongsToMany ao produto e aos modelos de usuários.
Listagem 16. Acrescente o relacionamento hasAndBelongsToMany ao produto e modelos de usuário
//In app/models/user.php:
<?php
class User extends AppModel
{
...
var $hasAndBelongsToMany = array( 'Product' =>
array(
'className' => 'Product'
)
);
}
?>
//In app/models/product.php:
<?php
class Product extends AppModel
{
...
var $hasAndBelongsToMany = array( 'User' =>
array(
'className' => 'User'
)
);
}
?>
|
Agora, sempre que um produto ou usuário for lido no banco de dados, ele vai verificar a tabela products_users para ver se há algum link que deve ser feito. Se encontrar linhas aplicáveis, vai carregá-las para os objetos correspondentes. Crie uma nova ação em users_controller.php chamada favoritos.
Listagem 17. Recuperando uma lista de favoritos
function favorites () {
$username = $this->Session->read('user');
$favorites = array();
if ($username)
{
$this->User->recursive = 2;
$results = $this->User->findByUsername($username);
foreach($results['Product'] as $product) {
$favorites[] = array('Product' => $product, 'Dealer' =>
$product['Dealer']);
}
$this->set('products', $favorites);
} else {
$this->redirect(array('controller' => 'users', 'action' => 'login'));
}
}
|
A ação addToFavorites
Do ponto de vista da interface, queremos que o usuário acrescente um produto aos seus favoritos. Portanto, precisamos de uma ação addToFavorites em ProductsController.
Abra o arquivo app/controllers/products_controller.php e acrescente a ação addToFavorites, mostrada abaixo.
Listagem 18. Acrescentando ação addToFavorites
function addToFavorites($id) {
$product = $this->Product->read(null, $id);
$username = $this->Session->read('user');
$success = false;
if ($this->Acl->check($username, $id.'-'.$product['Product']
['title'], 'read')) {
$result = $this->Product->User->findByUsername($username);
$product['User'] = array( 'User' =>
array($result['User']['id'])
);
$this->Product->save($product);
$success = true;
}
if ( $this->RequestHandler->isAjax() ) {
$this->layout = 'ajax';
if ( $success ) {
echo 'Added product to favorites';
} else {
echo 'Access denied';
}
exit;
} else {
if ( $success ) {
$this->Session->setFlash('Added product to favorites');
$this->redirect(array('controller' => 'users', 'action'
=> 'favorites'), null, true);
} else {
$this->Session->setFlash('Access denied');
$this->redirect(array('action' => 'index'), null, true);
}
}
}
|
Vejamos o que faz essa ação. Primeiro, ela faz uma verificação rápida na ACL para ver se o usuário realmente pode ler o produto. Depois, carrega o objeto de usuário com base no valor armazenado na variável de sessão user. Daí, formata os dados para salvar e salva o item na lista de favoritos.
Por fim, o Manipulador de Solicitação é chamado para ver se a ação foi uma solicitação do Ajax. Se foi, é retornada uma mensagem informativa. Se não, o usuário é redirecionado para a página apropriada.
Agora você pode ir para /products para testar a ação, dando o resultado mostrado na Figura 4. Veja a lista de produtos e escolha um por identificação, como o produto 8. Depois, vá para http://localhost/products/addToFavorites/8 e veja o resultado. Embora não haja outra evidência dele a não ser na mensagem de saída, o link foi feito. Conecte ao seu banco de dados e olhe a tabela products_users, e deverá ver uma única linha ligando seu user_id com o product_id selecionado.
Figura 4. Sucesso ao acrescentar produto 
Ligando à ação addToFavorites
Por fim, você deve fornecer um link para a ação addToFavorites na página de visualização de produtos. Você usará o método de link do auxiliar do Ajax para criar o link. O método, $ajax->link, age como o método $html->link. Ele usa os seguintes parâmetros:
$title — Uma cadeia de caractere que será usada para o título do link — no caso, "Excluir" $href — A ação que o link executará $options — Um array de opções, a mais importante das quais é a atualização, a identificação do elemento para o qual o texto de resposta deve ser encaminhado — no caso, estamos atualizando toda a tabela $confirm — Um alerta de JavaScript que vai abrir em pop-up, confirmando a ação
Esse método envia todo o JavaScript necessário para fazer uma solicitação Ajax completa. No fim, só precisaremos atualizar o controlador de produtos para atender a um novo tipo de solicitação. Utilizando o componente do Manipulador de Solicitação, mudamos o comportamento se a ação faz parte de uma chamada Ajax, como mostrado na Figura 18. Você também precisará acrescentar um teste à visualização de produtos para ver se o Ajax está habilitado e, se estiver, mostrar um link do Ajax. Esse link tentará atualizar um elemento DOM chamado favMessage. Você deve criá-lo em algum lugar da visualização, como no fim do produto div.
Listagem 19. Acrescentando a links favoritos na visualização de produtos
<?php
if ( isset($ajax) ) {
echo $ajax->link('Add to Favorites', array('action' => 'addToFavorites/' .
$product['Product']['id']), array('update' => 'favMessage'));
} else {
echo $html->link('Add to Favorites', array('action' => 'addToFavorites/' .
$product['Product']['id']));
}
?>
...
<span id='favMessage'> </span>
...
|
Para que isso funcione, você precisa acrescentar os auxiliares de JavaScript e Ajax ao controlador de Produtos, assim: var $helpers = array('Html', 'Form', 'Javascript', 'Ajax' );.
Usando o auxiliar do Ajax do CakePHP, acrescentamos a funcionalidade do Ajax sem ter de escrever uma única linha de JavaScript.
Preenchendo as falhas
Em vez de lhe dar o código para exibir a tabela de favoritos, vamos deixar você tentar isso sozinho. Você precisa acrescentar o método descrito antes favorites ao controlador de usuários e uma visualização de users/favorites.
Quando terminar com isso, tente acrescentar um link "Remover dos favoritos" à tabela de produtos. Configure-o de modo que o usuário seja mostrado no link "Acrescentar/Remover" com base em se o produto já está nos favoritos ou não.
|  |