KISS - Keep it simple, stupid!

Redirecionamento de Erros: descubra os 404 e diminua a insatisfação do usuário

postado por Lucas Gameiro em 22/03/2010 16:55:43

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

Tags: , , , , ,
Topo

4 Comentários

Creative Commons License
Sou Ágil: KISS em http://kiss.souagil.com.br está licenciado sobre
Creative Commons Attribution-Share Alike 2.5 Brazil License.

souÁgil