Redirecionamento de Erros: descubra os 404 e diminua a insatisfação do usuário
Uma das coisas mais frustrantes da navegação na internet é quando você vê um link que te interessa e quando clica é redirecionado pra um erro.
O 404 é o mais comum deles, isso porque ele é muito fácil de acontecer. Se alguém citou uma página do seu site que não existe mais, ou algum erro no script constroi uma url errada dinamicamente o 404 é invevitável você não vai percebê-lo rapidamente.
O CakePHP através do método link do helper Html já previne alguns problemas porém ainda não é possível administrar os erros de maneira fácil.
Pensando nisso, eu desenvolvi um sistema para informar quando os 404 acontecem e criar redirecionamentos para que eles não se repitam. Isto não é difícil de ser feito já que o CakePHP tem boas maneiras de controlar este erro.
A primeira coisa a se fazer é criar a tabela onde ficaram armazenados estes erros.
CREATE TABLE `redirects` ( `id` int(10) UNSIGNED NOT NULL AUTO_INCREMENT, `num_errors` int(10) UNSIGNED NOT NULL, `page_from` varchar(255) NOT NULL, `page_to` varchar(255), `num_redirects` int(10) UNSIGNED, PRIMARY KEY (`id`) )
Após a tabela criamos o modelo com métodos para contar e manipular os erros e os redirecionamentos: (/models/redirect.php)
<?php class Redirect extends AppModel { var $name = 'Redirect'; public function manageError($url){ $redirects = $this->find( 'first', array( 'conditions' => array('Redirect.page_from' => $url) ) ); if(!$redirects){ $redirects['Redirect']['num_errors'] = 0; $redirects['Redirect']['page_from'] = $url; $redirects['Redirect']['num_redirects'] = 0; $redirects['Redirect']['page_to'] = null; } $redirects['Redirect']['num_errors']++; $this->set( $redirects ); return $this->save(); } public function manageRedirect($urlfrom, $urlTo){ $redirect = $this->find( 'first', array( 'conditions' => array( 'Redirect.page_from' => $urlfrom ) ) ); $redirect['Redirect']['num_redirects']++; $this->set($redirect); return $this->save(); } } ?>
Com isso, já podemos criar o armazenamento, redirecionamento e contagem dos erros. Esta será feita escrevendo a classe AppError que é feita exatamente para manipular estes erros. (/app_error.php)
<?php class AppError extends ErrorHandler{ var $Error; public function error404($params){ $this->Redirect = ClassRegistry::init('Redirect'); $page = $this->Redirect->find( 'first', array( 'conditions' => array( 'page_from' => $params['url'] ) ) ); if($page && $page['Redirect']['page_to']){ $this->Redirect->manageRedirect( $params['url'], $page['Redirect']['page_to'] ); $Dispatcher = new Dispatcher(); $Dispatcher->dispatch($page['Redirect']['page_to']); } else{ $this->Redirect->manageError($params['url']); $this->controller->set('url', $params['url']); $this->_outputMessage('error404', $params); } } } ?>
Agora os erros já estão sendo contados e redirecionados porém sua administração tem que ser feita diretamente no banco de dados. É interessante portanto também criar métodos para gerenciar estes erros e redirecionamentos. ficamos então com o controller: (/controllers/redirects_controller.php)
<?php class RedirectsController extends AppController { var $name = 'Redirects'; var $helpers = array('Html', 'Form'); var $paginate = array( 'limit' => 25, 'order' => array( 'num_errors' => 'desc' ) ); function index() { $this->Redirect->recursive = 0; $this->set('redirects', $this->paginate()); } function create_redirect($id = null) { if (!$id && empty($this->data)) { $this->Session->setFlash(__('Redirecionamento Inválido', true)); $this->redirect(array('action'=>'index')); } if (!empty($this->data)) { if ($this->Redirect->save($this->data)) { $this->Session->setFlash(__('O Redirecionamento foi salvo.', true)); $this->redirect(array('action'=>'index')); } else { $this->Session->setFlash(__('O Redirecionamento não pode ser salvo. Por favor, tente novamente.', true)); } } if (empty($this->data)) { $this->data = $this->Redirect->read(null, $id); } } function delete($id = null) { if (!$id) { $this->Session->setFlash(__('Redirecionamento com id inválido', true)); $this->redirect(array('action'=>'index')); } if ($this->Redirect->del($id)) { $this->Session->setFlash(__('Redirecionamento excluído', true)); $this->redirect(array('action'=>'index')); } } } ?>
Observem que este controller não está utilizando nenhum método de autenticação. Fica em aberto o método a ser utilizado sendo que todas as páginas devem ficar invisíveis ao usuário comum. As views necessárias para este controller são:
create_redirect (/views/redirects/create_redirect.ctp):
<div class="redirects form"> <?php echo $form->create('Redirect', array('action' => 'create_redirect'));?> <fieldset> <legend><?php __('Criar Redirecionamento');?></legend> <?php echo $form->input('id'); echo $form->input('page_from', array('label'=>'URL do 404', 'disabled' => true)); echo $form->input('page_to', array('label' => 'Redirecionar para')); ?> </fieldset> <?php echo $form->end('Criar');?> </div> <div class="actions"> <ul> <li> <?php echo $html->link( __('Excluir', true), array( 'action' => 'delete', $form->value('Redirect.id') ), null, sprintf( __('Você tem certeza que deseja excluir # %s?', true), $form->value('Redirect.id') ) ); ?> </li> <li> <?php echo $html->link( __('Listar 404s', true), array('action' => 'index') ); ?> </li> </ul> </div>
e index (/views/redirects/index.ctp):
<div class="redirects index"> <h2><?php __('Redirecionamentos');?></h2> <table cellpadding="0" cellspacing="0"> <tr> <th><?php echo $paginator->sort('URL do 404', 'page_from');?></th> <th><?php echo $paginator->sort('número de erros', 'num_errors');?></th> <th><?php echo $paginator->sort('URL a redirecionar', 'page_to');?></th> <th><?php echo $paginator->sort('número de redirecionamentos', 'num_redirects');?></th> <th class="actions"><?php __('Ações');?></th> </tr> <?php foreach ($redirects as $redirect): ?> <tr> <td> <?php echo $redirect['Redirect']['page_from']; ?> </td> <td> <?php echo $redirect['Redirect']['num_errors']; ?> </td> <td> <?php echo $redirect['Redirect']['page_to']; ?> </td> <td> <?php echo $redirect['Redirect']['num_redirects']; ?> </td> <td class="actions"> <?php echo $html->link( __('Criar Redirecionamento', true), array( 'action' => 'create_redirect', $redirect['Redirect']['id'] ) ); ?> <?php echo $html->link( __('Excluir', true), array( 'action' => 'delete', $redirect['Redirect']['id'] ) ); ?> </td> </tr> <?php endforeach; ?> </table> </div> <div class="paging"> <?php echo $paginator->prev('<< '.__('anterior', true), array(), null, array('class'=>'disabled'));?> | <?php echo $paginator->numbers();?> <?php echo $paginator->next(__('próxima', true).' >>', array(), null, array('class' => 'disabled'));?> </div>
Pronto, agora temos uma ferramenta para gerenciar os erros 404 do site. Tomara que algum dia a frustração de pensar que um link solucionará meus problemas e tomar um 404 na lata diminua
Criei um projeto no github pra facilitar a baixar os arquivos. Está tudo neste link
Comentários (4)
-
001
Juan Basso
em 30/03/2010 08:11:50Lucas, ficou muito bom! Posso dar uma sugestão? Que tal empacotar num plugin isto? Com certeza muitos irão querer usar nas suas aplicações.
Aproveitando, para o Cake 1.3 eu sugeri (ticket #70) que erros internos (missingTable, missingDatabase e missingConnection) fossem respondidos com o código 500 (Internal Server Error) ao invés de 404 (Page not found). Isto foi aceito e está valendo me todos RCs. Talvez coubesse algumas adaptações devido a isto para o 1.3.
Se precisar de ajuda para componentizar, pode contar comigo.
Abraços e parabéns pelo ótimo trabalho.
-
002
Lucas Gameiro
em 30/03/2010 08:48:10Valeu pela dica Juan… pode deixar que vou transformar isso em um plugin sim…
Sobre o 1.3, acho que não vai dar muito trabalho pra adaptar já que tem a função error que é geral e não só para 404…
Obrigado, -
003
Adriano Bacha
em 30/03/2010 22:14:15muito bom o blog!
estou começando com BDD no cakephp e tenho tido muita dificuldade pra encontrar material a respeito -
004
Lucas Gameiro
em 31/03/2010 08:17:56Obrigado Adriano,
Sempre que possível tentamos soltar conteúdos sobre as práticas que a gente gosta ou gostaria de utilizar… Você faz parte do grupo de discussão do Google sobre CakePHP em PT? Se não, fica aí a dica… o pessoal realmente está pronto para se ajudar.
Abraços




