URL's Amigáveis com PHP e Apache – Um padrão de projeto de software

Certo dia um amigo que conheci através deste blog, Scott, me questionou sobre o método que eu utilizo para gerar os URL amigáveis.

Por este motivo me dispus a postar aqui de forma mais simplificada como obter o resultado desejado para se obter URL’s amigáveis.

Bom, a princípio, acho que há muita confusão quanto a este assunto porque existem várias formas de se obter o mesmo resultado e também uma tradução infeliz do Inglês se popularizou e piorou as coisas: Clean Url virou URL amigável.

Clean URL seria os URL’s que foram devidamente codificados para remover acentos, caracteres estranhos, espaços, tabulações e qualquer outra porcaria, a fim de não gerar incompatibilidades entre sistemas e idiomas ou e gerar uma coisa horrível como isto:

http://www.gilbertoalbino.com/URL%20Codificada%20com%20acentos%20%E1%E9%ED%F3%FA%20espa%E7o%20e%20outras%20porcarias

Se processássemos este URL com técnicas de URLs limpos, como ocorre com os links gerados neste blog (wordpress), ficaria assim:

http://www.gilbertoalbino.com/url-codificada-com-acentos-aeiou-espacos-e-outras-porcarias

Isto seria um Clean URL ou seja, um URL que foi limpo!

Existe outra situação, onde o URL ficaria assim:

http://www.gilbertoalbino.com/index.php?secao=tutorial&categoria=php&titulo=datetime

Isto seria um URL nem um pouco amigável! Logo, URL amigável é todo URL que permite ao usuário entender e se situar facilmente em determinada seção de um site.

Por exemplo:

http://www.gilbertoalbino.com/tutorial/php/datetime

Este URL me indicaria que há um tutorial sobre DateTime em PHP na seção de Tutoriais de meu site ( Não existe esta divisão em meu blog, é só uma situação hipotetica – risos ).

Certamente, gerar um código que controle estes dados se faz necessário, e respondendo à pergunta que todo mundo me faz, e atendendo à solicitação do amigo Scott, vou abordar primeiro o método que utilizo, que é baseado no padrão usado em MVC utilizando um roteador.

Padrão de Projeto de Software – Design Pattern?

O Padrão de Projeto de Software MVC, popular atualmente à quem está acostumado com os jargões da Programação Orientada à Objetos, faz com que você chame partes da aplicação, mais especificamente, controles e ações, através de URL amigáveis.

Se quiser uma rápida explicação sobre MVC clique aqui.

Poderíamos definir um Controle como um módulo que chamasse a área administrativa de um site ou aplicação web com admin.

Um exemplo:

http://gilbertoalbino.com/admin/

e

http://gilbertoalbino.com/admin/fotos/nova

Poderia ser o módulo de administração de fotos onde eu posso cadastrar novas fotos.

Em um website, poderíamos, também ter:

http://gilbertoalbino.com/contato/

http://gilbertoalbino.com/portifolio/

http://gilbertoalbino.com/sobre/

Então vamos pegar esta abordagem e criar um site usando esta estrutura primeiro, depois vamos simular uma aplicação.

NOTA: Para realizar este tutorial vamos precisar criar uma estrutura bem básica de arquivos

Crie as pastas libs e modulos em algum lugar num servidor apache.

  • libs (Vamos colocar aqui os arquivos que processam os URLs)
  • modulos ( Vamos colocar aqui os arquivos que constituem o site ou aplicação em si )

Configuração Básica do Apache

Todo a mágica precisa de uma boa varinha mágica (excesso de Harry Potter?).

Enfim, para todo e qualquer URL limpo funcionar é preciso que o servidor esteja configurado para redirecionamentos.

Vou simplesmente repetir os procedimentos que já fiz em um Tutorial em meu site.

Caso você esteja utilizando outro servidor que não seja o apache, você precisará verificar na documentação a forma mais correta de ativar o redirecionamento do mesmo.

Para o Apache, o procedimento é bem simples.

Como estou usando o Windows vou mostrar como procedi para obter o resultado desejado.

  1. Abra o arquivo httpd.conf do Apache, normalmente ele se encontra dentro da pasta conf
  2. Procure pela linha #LoadModule rewrite_module modules/mod_rewrite.so e retire o sustenido (#) inicial
  3. Procure pela Tag <Directory>
  4. Dentro desta troque Options FollowSymLinks por Options FollowSymLinks Includes
  5. Troque AllowOverride None por AllowOverride All
  6. Agora procure pela Tag <Directory “C:/Apache/htdocs”> que dependendo de sua instalação pode estar diferente, mas que é onde se encontra as configurações da pasta raiz do apache
  7. Tendo encontrado esta Tag, troque Options Indexes por Options Indexes FollowSymLinks
  8. Troque AllowOverride None por AllowOverride All
  9. No final do arquivo adicione a linha

    AccessFileName .htaccess e estamos como Apache configurado

No Ubuntu é fácil também, tenho um post dedicado à isto http://www.gilbertoalbino.com/linux-habilitar-mod_rewrite-no-ubuntu/.

Se estiver usando Wamp, Xamp ou sei lá o que empacotado, veja se encontra algo no google para tal!

Arquivo .htaccess

Ter habilitado o Apache para redirecionamentos não basta! É preciso informar quais regras ele deve utilizar.

Crie um arquivo chamado .htaccess dentro da raiz do site ou pasta da aplicação, se for o caso.

No caso de nosso tutorial, vou ser bem objetivo e direto:

Veja os comentários para entender…

 lang="javascript

# O valor "on" indica que o apache deve usar redirecionamento

RewriteEngine on

# se o arquivo existir na estrutura de arquivos,

# usar ele e não redirecionar

RewriteCond %{REQUEST_FILENAME} !-f

# se o diretório existir na estrutura de arquivos,

# usar ele e não redirecionar

RewriteCond %{REQUEST_FILENAME} !-d

# todo arquivo que não existir será

# redirecionado para o arquivo index.php

# poderia ser qualquer arquivo aqui,

# ele não ficará visível no navegador,

# somente o URL amigável,

# e o processo será feito por trás dos panos

# o parametro "r" no query string,

# será o único parametro do qual obteremos

# um valor a ser processado pela

# nossa classe de Roteamento que vamos criar

# L = Last Rule ( Ultima regra, ou seja, páre por aqui )

# QSA = Query String Append ( Query String Anexada )

# L, QSA é algo como: Ei, este é o query string que eu quero, páre de executar!

RewriteRule ^(.*)$ index.php?r=$1 [L,QSA]

Roteamento Uma Confusão com Bom Senso

Agora que já temos o apache e .htaccess configurado devidamente, precisamos, enfim, rotear as informações do URL amigável, e fazer nossa aplicação trabalhar em cima do que queremos.

Vamos usar o conceito de módulos para tal, todo URL processado terá seu primeiro parametro entre as barras entendido como “controlador” ou “modulo” e seu segundo parametro entre barras entendido como “acao“.

http://gilbertoalbino.com/controlador/acao

O controlador ou módulo é o nome do diretório per si, e a ação é o nome do arquivo.

Se o URL não conter o parametro para ação, a aplicação deverá entender que a ação é a index, ou página inicial, e incluirá o arquivo index que criaremos.

De igual forma, se não conter o parametro para o módulo, a aplicação entenderá que é a raiz do site ou pasta onde está o aplicação e incluirá o arquivo index do site ou aplicação.

Também é preciso entender que a aplicação enxergará o módulo e ação como um array ou índice numérico, onde 0 é o controlador e 1 ação, desta forma, se quisermos processar um ID, por exemplo, ao chamar um produto num site de vendas:

http://sitedevendas.com/produto/ver/567

Seria basicamente isto:



array(

    [0] = 'produto', // controlador

    [1] = 'ver', // acao

    [2] = 567 // id

)

Logo, a partir do paramento 1, você poderá usar o parametro 2 e seguintes para o que desejar, e usar a criatividade para elaborar aplicações mais complexas.

Para rotearmos e controlarmos nosso URL vamos criar uma classe Roteador e salvá-la dentro da pasta “libs“:



<?php

// libs/Roteador.php

class Roteador

{



    protected $uri = array();

    protected $controlador;

    protected $acao;



    public function __construct()

    {

        $this->parametros();



    }



    public function parametros()

    {

        $this->uri = ( isset( $_GET['r'] ) )

            ? explode( '/', $_GET['r'] )

            : array('');



    }



    public function parametro( $key )

    {

        if( array_key_exists( $key, $this->uri ) )

        {

            return $this->uri[$key];

        } else {

            return false;

        }

    }



    public function controlador()

    {

        $this->controlador = ( $this->uri[0] == NULL )

            ? 'index'

            : $this->uri[0] ;



        return ( is_string( $this->controlador ) )

            ? $this->controlador

            : 'index';



    }



    public function acao()

    {

        $this->acao = (

                isset( $this->uri[1] )

                && strlen( $this->uri[1] ) != 0

                && is_string( $this->uri[1] )

            )

            ? $this->uri[1]

            : 'index' ;



        return $this->acao;



    }

}

Agora que já estamos aqui é hora de criar nosso arquivo “index.php” e salvá-lo na mesma pasta do arquivo “.htaccess“.



<meta charset="utf-8" />

<?php

/**

 * Carregamos a classe, senão a coisa não rola!

 */

require_once 'libs/Roteador.php';



/**

 * Criamos uma instancia para começarmos a brincar

 */

$roteador = new Roteador();



/**

 * Teste básico de funcionamento

 */

print 'Controlador: ' . $roteador->obtemControlador();

print '<br />';

print 'Ação: ' . $roteador->obtemAcao();



/**

 * Entender o funcionamento da socilitacao via GET

 */

print '<pre>';

print_r($_GET);

print '</pre>';

Agora acesse a raiz do site ou aplicação, e você terá o seguinte resultado



Controlador: index

Ação: index

Array

(

)

Note que ambos foram entendidos como “index”, e GET está vazio!

Vamos testar nossos controladores, chamando um controlador qualquer:

http://caminhoqualquer/teste

Agora o resultado será totalmente diferente:



Controlador: teste

Ação: index

Array

(

    [r] => teste

)

Note que o controlador mudou para teste, e GET agora tem “teste”, mas não tem nada com index para ação. Isto se dá, porque quem tá controlando é a class e Roteador, e não o servidor!

Vamos testar nossos controladores e ações chamando ambos de qualquer forma:

http://caminhoqualquer/teste/outro

O resultado ficou mais interessante agora:



Controlador: teste

Ação: outro

Array

(

    [r] => teste/outro

)

Note que controlador e ação apareceram e que GET mostrou ambos juntos separados por uma barra exatamente igual ao que passamos no URL.

De fato, é isto mesmo, agora faça os testes que quiser colocando mais valores depois do controlador e da ação separados por barras, e veja que eles vão se manter os mesmos sempre, e o que vai mudar é o GET, e para obter estes valores agora vamos usar outro método de nossa classe.

Apague tudo o que está dentro do arquivo index.php e substitua por:



<meta charset="utf-8" />

<?php

/**

 * Carregamos a classe, senão a coisa não rola!

 */

require_once 'libs/Roteador.php';



/**

 * Criamos uma instancia para começarmos a brincar

 */

$roteador = new Roteador();



/**

 * Teste básico de funcionamento

 */

print 'Parametro 0: ' . $roteador->controlador() . ' ( controlador )';

print '<br />';

print 'Parametro 1: ' . $roteador->acao() . ' ( acao )';



/**

 * Entender o funcionamento da socilitacao via GET

 */

print '<pre>';

print_r($_GET);

print '</pre>';



/**

 * Teste de parametro isolado

 */



print 'Parametro 2: ' . $roteador->parametro(2);

print '<br />';

print 'Parametro 3: ' . $roteador->parametro(3);

print '<br />';

print 'Parametro 4: ' . $roteador->parametro(4);

print '<br />';

Agora chame sua aplicação desta forma:

http://caminhoqualquer/teste/outro/123/456/789

E veja o resultado!

É só usar a criatividade com os resultados depois…

Nota: Como o Apache está parando o processamento no parametro “r“, isto significa que podemos usar outros parametros GET à vontade.

Agora você deve estar se perguntando: “Mas pra que eu faria isto, já que quero um URL limpo e amigável?”

Bom, você tem razão, mas tem situações que é impossível de se usar 100% URLs amigáveis!

Considere o caso onde você tem uma busca em um site e precisa paginar os resultados.

Fazer um sistema de buscas totalmente em URLs amigáveis é praticamente inviável e dificil demais, e não não tem necessidade, uma vez que o usuário não precisa guardar na cabeça o url de uma busca!

Criando os módulos do site

Agora que, acredito, você já tenha entendido como tudo está funcionando, vamos organizar o conteúdo de nosso site com tudo isto.

Crie as pastas “index, contato, portifolio e sobre” dentro de modulos.

Porque criar a pasta index? Bom, porque a raiz do site ou aplicação também é um módulo!

Ficamos com a estrutura:

  • libs
  • modulos
    • contato
    • index
    • portifolio
    • sobre

Dentro de cada pasta crie um arquivo chamado “index.php” e escreva algo indicando que você está naquele arquivo. Veja o fonte do projeto que está disponível para download no início deste post.

Crie um arquivo 404.php na mesma pasta de “.htaccess“, para no caso de você ter apagado um arquivo sem querer, aparecer uma mensagem amigável, e escreva algo indicando que aquela página não está disponível no momento!

Eu normalmente uso a extensão “.phtml“, pois acho mais interessante, pois lembra arquivo de template em PHP.

Fique à vontade para escolher qualquer extensão, mas vale lembrar que .html não processa PHP e .inc, .tpl podem não oferecer os recursos de intellisense ou autocompletion de código para editores tipo Netbeans ou Eclipse.

Vamos deixar as coisas bem simples, e criar uma forma de controlar o fluxo da aplicação, incluindo os arquivos de acordo com o controlador e a ação.

Lembrando que a ação, por padrão é index, caso tenhamos somente o controlador, e por isto precisamos do arquivo index dentro de cada pasta, que seriam os módulos, chamados pelos controladores da classe Roteador.

Após ter criado a estrutura de arquivos acima, apague tudo do arquivo index.php e coloque o conteúdo a seguir:



<?php

/**

 * Carregamos a classe, senão a coisa não rola!

 */

require_once 'libs/Roteador.php';

/**

 * Criamos uma instancia para começarmos a brincar

 */

$roteador = new Roteador();

/**

 * Se você estiver dentro de uma pasta no servidor

 * O html não tem como saber isto, então temos que dar uma mãozinha

  */

$dir =  '/tutorial';

?>

<!DOCTYPE html>

<html lang="pt-br">

    <head>

        <title>URL Limpo ou Amigável?</title>

        <meta charset="utf-8" />

    </head>

    <body>

        <div><h1>Cabeçalhos</h1></div>

        <div>

            <ul>

                <li><a href="<?=$dir;?>">Home</a></li>

                <li><a href="<?=$dir;?>sobre">Sobre</a></li>

                <li><a href="<?=$dir;?>portifolio">Portifolio</a></li>

                <li><a href="<?=$dir;?>contato">Contato</a></li>

            </ul>

        </div>

        <div>

            <h2>Conteúdo</h2>

            <?php

                $controlador = $roteador->controlador();

                $acao = $roteador->acao();



                $modulo = (

                        is_dir(

                            'modulos'

                            . DIRECTORY_SEPARATOR

                            . $controlador

                        )

                    )

                    ? 'modulos'

                        . DIRECTORY_SEPARATOR

                        . $controlador

                    : 'modulos'

                        . DIRECTORY_SEPARATOR

                        . 'index';



                $pagina = (

                        is_file(

                            $modulo

                            . DIRECTORY_SEPARATOR

                            . $acao . '.php'

                        )

                    )

                    ? $acao . '.php'

                    : 'index.php';



                if(

                    is_file(

                        $modulo

                        . DIRECTORY_SEPARATOR

                        . $pagina

                    )

                ) {

                    include_once $modulo

                        . DIRECTORY_SEPARATOR

                        . $pagina;

                } else {

                    include_once '404.php';

                }



            ?>

        </div>

        <div><h3>Rodapé</h3></div>

    </body>

</html>

NOTA: O caminho a ser usado na tag A do HTML tem que ser o caminho da pasta dentro do servidor, por este motivo, criamos a variável $dir, pois o HTML não tem como saber que estamos redirecionando, e se você estiver dentro de uma pastar qualquer, ele vai jogar para a raiz do site. Por isto, se estiver na raiz do servidor coloque NULL, ou “”.

Já temos nosso sistema de URL amigáveis funcionando. ( Ou deveria estar kkkk ).

Bom, até ai tudo bem, daria para fazer isto colocando um arquivo index dentro de cada pasta e pronto.

Mas, vamos, brincar um pouco?

Suponhamos que em seu portifólio existam “sites” e “impressos“.

Você poderia desejar uma pagina para cada qual, não é?

Vamos modificar o menu que temos e deixar ele desta forma:



        <div>

            <ul>

                <li><a href="<?=$dir;?>">Home</a></li>

                <li><a href="<?=$dir;?>/sobre">Sobre</a></li>

                <li><a href="<?=$dir;?>/portifolio">Portifolio</a>

                    <ul>

                        <li><a href="<?=$dir;?>/portifolio/webdesign">Web Design</a></li>

                        <li><a href="<?=$dir;?>/portifolio/impressos">Impressos</a></li>

                    </ul>

                </li>

                <li><a href="<?=$dir;?>/contato">Contato</a></li>

            </ul>

        </div>

Agora basta criar os arquivos “/modulos/portifolio/webdesign.php” e “/modulos/portifolio/impressos.php” que ao chamarmos eles clicando no link, vamos automaticamente exibir o conteúdo deles.

Conclusão!

Bom, eu tive que simplificar muita coisa, pois eu uso um MVC completo para isto, que levei meses para desenvolver, e certamente, não caberia neste tutorial, então me limitei a mastigar a ideia e deixar as implementações aos amigos!

É isto ai, use a criatividade e coloque seu potencial em ação!

Se acharem algum erro no tutorial me avisem postando nos comentários!

Dedicado e com atenção ao amigo Rogieri Batista Gonçalves de Sousa – Scott!

6 comentários sobre “URL's Amigáveis com PHP e Apache – Um padrão de projeto de software”

  1. Boa Noite Gilberto ai valeu cara, ai voc? mencionou que estava fazendo algumas altera??es no seu Blog e resolvi olhar, ai valeu por postar o tutorial sobre Url’s amig?veis….. Tenho aprendido muito com voc? Gilberto, obrigado pela considera??o cara……..

  2. Boa tarde, Gilberto! O único problema que tenho ao separar parâmetros com barra (/) é que buga todas as referências que faço a arquivos .css e .js por exemplo, pois o navegador interpreta como um diretório. Sabe como resolver isso?

    Abraços!

    1. Se você está utilizando URLs amigáveis no apache, voce deve colocar o caminho completo para o arquivo CSS ou Javascript precedido com uma barra na frente.

      corresponderia à pasta /js na raiz

      Já no Css, para chamar o arquivo é a mesma coisa, porém, para passar caminhos dentro do arquivo você pode colocar algo do tipo background: url(../img/bg.png) , no caso se a pasta /public/img estiver no mesmo nível de /public/css

      Você poderia, especificar se problema?

Deixe uma resposta

O seu endereço de e-mail não será publicado. Campos obrigatórios são marcados com *