ZF2 Obtenha um RouteMatch de uma rota específica


Se você está desenvolvendo sua aplicação utilizando o Zend Framework 2 e se deparou com o problema de não conseguir obter informações específicas via RouteMatch de uma rota em string que tem em mãos, talvez este post possa te ajudar.

Cenário

O cenário que encontrei era o seguinte: Ao desenvolver um módulo ACL customizado, precisava verificar em certas ocasiões se um determinado “recurso” estava permitido para o perfil do usuário autenticado.

Para manter um certo padrão, dei preferência em manter a mesma sintaxe da utilizada pelo ViewHelper para gerar URLs. Se você não está lembrado ou não conhece, confira um exemplo abaixo:

<a href="<?php echo $this->url('myroute', array('myparam' => 1));">Link</a>

Como minhas regras de ACL se baseavam em nomes de Controller e em suas respectivas Action, eis que surge um problemão. O motivo é: ambas as informações podem ser obtidas através de uma rota a partir do RouteMatch. Por padrão dá para obter o RouteMatch correspondente à URL atual em um MvcEvent, algo como o trecho a seguir.

public function onBootstrap(MvcEvent $e)
{
  $match = $e->getRouteMatch();
}

Tendo o RouteMatch é possível obter o nome do Controller via o método getParam('controller') e o nome da Action via o método getParam('action').

A API do Router

A primeira coisa que vem à cabeça é correr para o StackOverflow e Google caçando algo relacionado à API do Router interno do Zend Framework. O problema é que você se deparará com o seguinte: o único método que obtém um RouteMatch recebe como parâmetro um Request… WTF?

Vamos destrinchar um pouco disso, confira abaixo a interface RouteInterface, que é o “contrato” dos Routers do Zend Framework 2:

interface RouteInterface
{
    public static function factory(array $options = array());
    public function match(Request $request);
    public function assemble(array $params = array(), array $options = array());
}

Os métodos que são pertinentes a nós são os dois últimos:

  • match(Request $request) - Verifica nas rotas disponíveis a que corresponde à requisição e retorna um RouteMatch com as principais informações da rota encontrada
  • assemble(...) - “Casa” parâmetros correspondentes a uma rota, como, por exemplo, um nome, podendo também fornecer informações extras referentes a subrotas, e retorna a URI da rota encontrada.

Obtendo o RouteMatch

Depois de explicar o propósito dos dois métodos da API RouteInterface talvez você já tenha pensado na solução que vou propor, se não pensou tudo bem, aqui vou explicá-la um pouco melhor.

Primeiramente será necessário obter o Router.

$router = $this->getServiceLocator()->get('Router');

Logo no início descrevi que usaria uma API similar a do ViewHelper de URLs, que usa o nome da rota. Portanto o próximo passo é obter a URI correspondente a esse nome de rota através do método assemble().

$routeUri = $router->assemble('myroute');

Isso é necessário porque para obter o RouteMatch precisaremos construir um Request correspondente à URI que desejamos obter as informações.

use Zend\Http\PhpEnvironment\Request;

$request = new Request();
$request->setUri($routeUri);

Pronto, agora já é possível obter o que precisamos da rota a partir de um RouteMatch.

$routeMatch = $router->match($request);

Coisas como o nome do Controller, Action e parâmetros extras agora estão disponíveis.

$controllerName = $routeMatch->getParam('controller');
$actionName = $routeMatch->getParam('action');

Para validar com os seus privilégios de ACL, apesar de não ser o escopo deste post, já pode usar algo como o código abaixo.

if ($myAcl->isAllowed($user->getRole(), $controllerName, $actionName)) {
  // allowed
} else {
  // restricted
}

Mais informações

Até a próxima!