Routes do CakePHP com banco de dados Artigo

Conheça os cursos gratuitos do WebDevBr! - Inscreva-se!


Este artigo foi publicado a 4 anos, 2 dias atrás.

Se tem algo que não tem muito material na internet é sobre um roteamento com banco de dados no CakePHP, não que já não seja flexível o suficiente, mas sempre da pra melhorar, e é ai que entra o routeClass.

O que é routeClass

RouteClass é um recurso do CakePHP que permite passarmos a url para uma classe PHP analisar e responder com um array que indique qual Controller, Action e parâmetros devemos usar, caso retorne false ele vai ignorar e passar para a próxima regra do routes.php (app/Config/routes.php).

Como o routes trabalha

Para você entender melhor o que eu disse acima vou dar um exemplo, imagine que você tem a seguintes rotas no routes.php:

Gostou deste artigo?

Receba atualizações semanais com novos artigos do WebDevBr e outras dicas!

Router::connect('/', array('controller' => 'pages', 'action' => 'display', 'home'));
Router::connect('/empresa', array('controller' => 'pages', 'action' => 'display', 'empresa'));
Router::connect('/contato', array('controller' => 'pages', 'action' => 'display', 'contato'));

Quando o CakePHP le as rotas ele começa no primeiro item (que no exemplo é a home do site) se a url passada conferir com '/' ele deverá retornar o array indicando qual Controller, Action e parâmetros devem ser usados, a checagem para aqui e não lê a demais rotas, agora se não conferir ele retorna false e passa para o próximo item, isso quer dizer que os itens superiores tem a prioridade. No exemplo abaixo, se eu visitar a página inicial da aplicação o segundo parâmetro não teria serventia alguma.

Router::connect('/', array('controller' => 'pages', 'action' => 'display', 'home'));
Router::connect('/', array('controller' => 'paginainicial', 'action' => 'display'));

Em outras palavras o controller "paginainicial" nunca seria requisitado, já que o CakePHP pararia de pesquisar a rota um item antes.

Construindo uma classe de Roteamento

Claro que vou passar a classe pronta pra usar no fim do artigo, então essa parte é opcional.

Quando começamos a estudar CakePHP logo de cara já conhecemos as pastas Config, Controller, Model e View, algumas outras também são bem conhecidas, mas essas 4 formam o básico do básico, e mesmo depois de algum tempo trabalhando com o Cake você pode vir a notar que não usa a pasta Lib, pois bem, quando você quer incrementar o Core do seu CakePHP é aqui que o arquivo fica, então é isso que vamos fazer aqui, pra começar crie uma estrutura de diretórios:

  • Routing
    • Route

E dentro da pasta Route o seu arquivo DbRoute.php, a convenção do CakePHP nos diz que os arquivos de roteamento tem que ser prefixados pelo nome Route e estender a classe CakeRoute, neste caso minha classe se chamaria Db, então o nome final é DbRoute. Dentro do arquivo adicione a classe DbRoute e estenda a CakeRoute:

class DbRoute extends CakeRoute
{
}

Também vou usar o utility ClassRegistry para carregar o Model (afinal vamos trabalhar com banco de dados, então preciso de um model pra isso), carregue ele antes de abrir a classe, assim:

App::uses('ClassRegistry', 'Utility');

class DbRoute extends CakeRoute
{
}

Toda classe de roteamento tem dois métodos (funções) já pré-definidas e você precisa usar pelo menos um dos dois (ou os dois), eles são:

  • parse: Usado para analisar as requisições, é ele quem retorna o array da rota ou o false
  • match: Usado para tratar o roteamento reverso

Nós vamos usar o parse apenas, vou aproveitar e criar mais dois métodos:

  • returnSlug: Vai verificar se a url veio vazia, e neste caso é a home da aplicação, então ele vai setar o '/' como url
  • getPass: Vai retornar os parâmetros para o controller, o CakePHP vai usar a chave de array "pass" para pegar este argumento, então por isso o nome getPass

Para informar qual controller, action e parâmetros vamos passar para o CakePHP vamos usar um array com a seguinte estrutura:

Array(
    'controller'=>'Controller',
    'action'=>'Action',
    'pass'=>Array(
        0=>'Parâmetro 1',
        1=>'Parâmetro 2',
        2=>'Parâmetro 3',
    )
)

Vamos ao código completo da nossa nossa classe:

<?php

App::uses('ClassRegistry', 'Utility');

class DbRoute extends CakeRoute
{

    protected function returnSlug($slug)
    {
        if (empty($slug)) {
            return '/';
        }

        return $slug;
    }

    protected function getPass($slug)
    {
        if (preg_match('/^(/).{1,}/',$slug)) {
            $slug = substr($slug,1);
        }
        if (preg_match('/.{1,}(/)$/',$slug)) {
            $slug = substr($slug,0,-1);
        }
        return explode('/',$slug);
    }

    public function parse($slug)
    {
        $slug = $this->returnSlug($slug);
        $route = ClassRegistry::init('Route');
        $url=$route->find('first',array('conditions'=>array('Route.slug'=>$slug)));

        if(empty($url)) {
            return false;
        } else {
            $parse['controller']=$url['Route']['controller'];
            $parse['action']=$url['Route']['action'];
            $parse['pass']=$this->getPass($url['Route']['pass']);
            if (empty($parse['pass'])) {
                unset($parse['pass']);
            }
            return $parse;
        }

    }
}

Então $slug é nossa url.

No método returnSlug() está simples, se estiver vazio é '/', e retorna o url.

O método getPass() verifica se existe "/" no começo e no fim da string e remove para evitar que gere uma entrada vazia no começo do array e outra no fim e depois transforma a string em array e retorna esse array.

E por fim o método parse() que faz a mágica toda então vou comentar linha a linha:

public function parse($slug)
{
    //Usa o returnSlug() para retornar a url correta mesmo que esteja vazio
    $slug = $this->returnSlug($slug);

    //Inicia o Model Route na variavel $route
    $route = ClassRegistry::init('Route');

    //Executa um find para buscar no banco o controller e action dessa url e guarda em $url
    $url=$route->find('first',array('conditions'=>array('Route.slug'=>$slug)));

    //se não encontrar nada (neste caso a variável $url vem vazia)
    if(empty($url)) {

        //retornar false, para avisar que é pra continuar a checar as outras rotas do routes.php
        return false;
    } else {
        //se encontrar algum resultado no campo
        //organizo tudo na variável que será repassada ao Cake (falamos dela acima)
        $parse['controller']=$url['Route']['controller'];
        $parse['action']=$url['Route']['action'];
        //aqui eu pego o retorno da getPass() e armazeno na variável também
        $parse['pass']=$this->getPass($url['Route']['pass']);
        //se a ['pass'] estiver vazia eu removo do array
        if (empty($parse['pass'])) {
            unset($parse['pass']);
        }
        //retorno a variável para o Cake
        return $parse;
    }

}

Pra fechar essa etapa, faltou apenas o banco de dados:

CREATE TABLE IF NOT EXISTS `routes` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `slug` varchar(255) NOT NULL,
  `controller` varchar(20) NOT NULL,
  `action` varchar(20) NOT NULL,
  `pass` varchar(55) NOT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1 AUTO_INCREMENT=1 ;

Como usar o DbRoute no meu route.php

É muito fácil, primeiro você precisa carregar o DbRoute e em seguida usá-lo:

App::uses('DbRoute', 'Routing/Route');
Router::connect(':slug', array(),array('routeClass' => 'DbRoute'));

Lembrando que você pode colocar o DbRoute primeiro ou em último lugar no routes.php, depende da sua lógica.

Como criar novas rotas no banco de dados.

É muito fácil, é só você usar relacionamentos, neste caso vai depender de como você quer, vou usar de exemplo um cadastro de Páginas:

  • Muitas Páginas tem muitas URLs: HABTM
  • Uma página tem muitas URLS: HasMany
  • Uma página tem uma URL: HasOne

No caso de páginas, o mais indicado seria o HasOne e na hora de salvar você criar a url a partir do título da página direto no beforeSave(), mas ai é com você.

Como baixar o DbRoute

Eu subi o DbRoute no GitHub pra vocês, ajudem lá.

O link: https://github.com/erikfig/DbRouteCakePHP

Do "ladinho" tem um botão chamado Download ZIP pra quem não quer usar o GitHub.

Bom proveito.


Cursos relacionados


* Parcelamento apenas cartão de crédito! Pode haver uma pequena variação no parcelamento em relação a simulações apresentadas!