Roteiro
O roteador é responsável por tudo sobre URLs, para que você não tenha mais que pensar nelas. Nós vamos mostrar:
- como configurar o roteador para que as URLs pareçam como você quer
- algumas notas sobre o redirecionamento de SEO
- e nós lhe mostraremos como escrever seu próprio roteador
URLs mais humanas (ou mais legais ou bonitas) são mais utilizáveis, mais memoráveis e contribuem positivamente para SEO. Nette tem isto em mente e atende plenamente aos desejos dos desenvolvedores. Você pode projetar sua estrutura de URLs para sua aplicação exatamente do jeito que você quiser. Você pode até projetá-la depois que o aplicativo estiver pronto, pois pode ser feita sem qualquer mudança de código ou modelo. Ela é definida de forma elegante em um único lugar, no roteador, e não está dispersa na forma de anotações em todos os apresentadores.
O roteador em Nette é especial porque é bidirecional, ele pode tanto decodificar URLs de solicitação HTTP quanto criar links. Portanto, ele desempenha um papel vital na Aplicação Nette, pois decide qual apresentador e ação executará a solicitação atual, e também é utilizado para geração de URLs no modelo, etc.
Entretanto, o roteador não está limitado a este uso, você pode usá-lo em aplicações onde os apresentadores não são usados, para APIs REST, etc. Mais na seção uso separado.
Coleta de rotas
A maneira mais agradável de definir os endereços URL na aplicação é através da classe Nette\Application\Routers\RouteList. A definição consiste em uma lista das chamadas rotas, ou seja, máscaras de endereços URL e seus apresentadores associados e ações utilizando uma API simples. Não é necessário nomear as rotas.
$router = new Nette\Application\Routers\RouteList;
$router->addRoute('rss.xml', 'Feed:rss');
$router->addRoute('article/<id>', 'Article:view');
// ...
O exemplo diz que se abrirmos https://any-domain.com/rss.xml
com a ação rss
será exibido, se
https://domain.com/article/12
com a ação view
será exibido, etc. Se nenhum caminho adequado for
encontrado, a Aplicação Nette responde lançando uma exceção BadRequestException, que aparece ao
usuário como uma página de erro 404 Não encontrado.
Ordem das Rotas
A ordem na qual as rotas são listadas é muito importante porque são avaliadas seqüencialmente de cima para baixo. A regra é que declaramos as rotas **de específicas a gerais***:
// ERRADO: 'rss.xml' corresponde à primeira rota e entende isto mal como <slug>
$router->addRoute('<slug>', 'Article:view');
$router->addRoute('rss.xml', 'Feed:rss');
// BOM
$router->addRoute('rss.xml', 'Feed:rss');
$router->addRoute('<slug>', 'Article:view');
As rotas também são avaliadas de cima para baixo quando os links são gerados:
// ERRADO: gera um link para 'Feed:rss' como 'admin/feed/rss'.
$router->addRoute('admin/<presenter>/<action>', 'Admin:default');
$router->addRoute('rss.xml', 'Feed:rss');
// BOM
$router->addRoute('rss.xml', 'Feed:rss');
$router->addRoute('admin/<presenter>/<action>', 'Admin:default');
Não vamos esconder de você que é preciso alguma habilidade para construir uma lista corretamente. Até que você entre nela, o painel de roteamento será uma ferramenta útil.
Máscara e Parâmetros
A máscara descreve o caminho relativo com base na raiz do local. A máscara mais simples é uma URL estática:
$router->addRoute('products', 'Products:default');
Muitas vezes as máscaras contêm os chamados **parâmetros***. Elas estão entre parênteses angulares (por exemplo
<year>
) e são passadas ao apresentador alvo, por exemplo, ao método renderShow(int $year)
ou ao
parâmetro persistente $year
:
$router->addRoute('chronicle/<year>', 'History:show');
O exemplo diz que se abrirmos https://any-domain.com/chronicle/2020
e a ação show
com
o parâmetro year: 2020
serão exibidos.
Podemos especificar um valor padrão para os parâmetros diretamente na máscara e assim ele se torna opcional:
$router->addRoute('chronicle/<year=2020>', 'History:show');
A rota agora aceitará a URL https://any-domain.com/chronicle/
com o parâmetro year: 2020
.
Naturalmente, o nome do apresentador e a ação também podem ser um parâmetro. Por exemplo, o nome do apresentador e a ação também podem ser um parâmetro:
$router->addRoute('<presenter>/<action>', 'Home:default');
Esta rota aceita, por exemplo, uma URL no formulário /article/edit
resp. /catalog/list
e as traduz
para os apresentadores e ações Article:edit
resp. Catalog:list
.
Também dá aos parâmetros presenter
e action
valores padrãoHome
e
default
e, portanto, são opcionais. Portanto, a rota também aceita uma URL /article
e a traduz como
Article:default
. Ou vice versa, um link para Product:default
gera um caminho /product
, um
link para o padrão Home:default
gera um caminho /
.
A máscara pode descrever não apenas o caminho relativo baseado na raiz do site, mas também o caminho absoluto quando ele começa com uma barra, ou mesmo todo o URL absoluto quando começa com duas barras:
// caminho relativo à raiz do documento de aplicação
$router->addRoute('<presenter>/<action>', /* ... */);
// caminho absoluto, relativo ao nome da hostname do servidor
$router->addRoute('/<presenter>/<action>', /* ... */);
// URL absoluto, incluindo o nome da hostname (mas esquema-relativo)
$router->addRoute('//<lang>.example.com/<presenter>/<action>', /* ... */);
// URL absoluta, incluindo o esquema
$router->addRoute('https://<lang>.example.com/<presenter>/<action>', /* ... */);
Expressões de validação
Uma condição de validação pode ser especificada para cada parâmetro usando expressão regular. Por exemplo, vamos definir
id
para ser apenas numérico, usando \d+
regexp:
$router->addRoute('<presenter>/<action>[/<id \d+>]', /* ... */);
A expressão regular padrão para todos os parâmetros é [^/]+
ou seja, tudo, exceto a barra. Se um parâmetro
deve corresponder a uma barra também, definimos a expressão regular para .+
.
// aceita https://example.com/a/b/c, o caminho é 'a/b/c'.
$router->addRoute('<path .+>', /* ... */);
Seqüências Opcionais
Colchetes quadrados denotam partes opcionais da máscara. Qualquer parte da máscara pode ser definida como opcional, incluindo aquelas que contenham parâmetros:
$router->addRoute('[<lang [a-z]{2}>/]<name>', /* ... */);
// URLs aceitas: Parâmetros:
// /en/download lang => en, name => download
// /download lang => null, name => download
Naturalmente, quando um parâmetro faz parte de uma seqüência opcional, ele também se torna opcional. Se não tiver um valor padrão, ele será nulo.
As seções opcionais também podem estar no domínio:
$router->addRoute('//[<lang=en>.]example.com/<presenter>/<action>', /* ... */);
As seqüências podem ser livremente aninhadas e combinadas:
$router->addRoute(
'[<lang [a-z]{2}>[-<sublang>]/]<name>[/page-<page=0>]',
'Home:default',
);
// URLs aceitas:
// /en/hello
// /en-us/hello
// /hello
// /hello/page-12
O gerador de URL tenta manter a URL o mais curta possível, de modo que o que pode ser omitido é omitido. Portanto, por
exemplo, uma rota index[.html]
gera um caminho /index
. Você pode reverter este comportamento escrevendo
um ponto de exclamação após o colchete à esquerda:
// aceita /hello e /hello.html, gera /hello
$router->addRoute('<name>[.html]', /* ... */);
// aceita tanto /hello como /hello.html, gera /hello.html
$router->addRoute('<name>[!.html]', /* ... */);
Os parâmetros opcionais (ou seja, parâmetros com valor padrão) sem parênteses rectos comportam-se como se fossem embrulhados desta forma:
$router->addRoute('<presenter=Home>/<action=default>/<id=>', /* ... */);
// é igual a:
$router->addRoute('[<presenter=Home>/[<action=default>/[<id>]]]', /* ... */);
Para mudar a forma como a barra mais à direita é gerada, ou seja, ao invés de /home/
, obtenha um
/home
, ajuste a rota desta forma:
$router->addRoute('[<presenter=Home>[/<action=default>[/<id>]]]', /* ... */);
Wildcards
Na máscara do caminho absoluto, podemos usar os seguintes wildcards para evitar, por exemplo, a necessidade de escrever um domínio para a máscara, que pode diferir no ambiente de desenvolvimento e produção:
%tld%
= domínio de primeiro nível, por exemplocom
ouorg
%sld%
= domínio de segundo nível, por exemploexample
%domain%
= domínio sem subdomínios, p. ex.example.com
%host%
= host inteiro, p.ex.www.example.com
%basePath%
= caminho para o diretório raiz
$router->addRoute('//www.%domain%/%basePath%/<presenter>/<action>', /* ... */);
$router->addRoute('//www.%sld%.%tld%/%basePath%/<presenter>/<action', /* ... */);
Notação avançada
O destino de uma rota, geralmente escrito no formato Presenter:action
, também pode ser expresso usando uma
matriz que define parâmetros individuais e seus valores padrão:
$router->addRoute('<presenter>/<action>[/<id \d+>]', [
'presenter' => 'Home',
'action' => 'default',
]);
Para uma especificação mais detalhada, é possível usar um formulário ainda mais extenso, no qual, além dos valores
padrão, outras propriedades de parâmetro podem ser definidas, como uma expressão regular de validação (consulte o parâmetro
id
):
use Nette\Routing\Route;
$router->addRoute('<presenter>/<action>[/<id>]', [
'presenter' => [
Route::Value => 'Home',
],
'action' => [
Route::Value => 'default',
],
'id' => [
Route::Pattern => '\d+',
],
]);
É importante observar que, se os parâmetros definidos na matriz não estiverem incluídos na máscara de caminho, seus valores não poderão ser alterados, nem mesmo usando parâmetros de consulta especificados após um ponto de interrogação no URL.
Filtros e Traduções
É uma boa prática escrever o código fonte em inglês, mas e se você precisar que seu website tenha o URL traduzido para outro idioma? Rotas simples, como por exemplo:
$router->addRoute('<presenter>/<action>', 'Home:default');
gerará URLs em inglês, tais como /product/123
ou /cart
. Se quisermos ter apresentadores e ações
na URL traduzidas para o Deutsch (por exemplo /produkt/123
ou /einkaufswagen
), podemos usar um
dicionário de tradução. Para adicioná-lo, já precisamos de uma variante “mais faladora” do segundo parâmetro:
use Nette\Routing\Route;
$router->addRoute('<presenter>/<action>', [
'presenter' => [
Route::Value => 'Home',
Route::FilterTable => [
// string na URL => apresentador
'produkt' => 'Product',
'einkaufswagen' => 'Cart',
'katalog' => 'Catalog',
],
],
'action' => [
Route::Value => 'default',
Route::FilterTable => [
'liste' => 'list',
],
],
]);
As chaves de múltiplos dicionários podem ser usadas para o mesmo apresentador. Elas criarão vários pseudônimos para ele. A última chave é considerada como a variante canônica (ou seja, aquela que estará na URL gerada).
A tabela de tradução pode ser aplicada a qualquer parâmetro desta forma. Entretanto, se a tradução não existir, é
tomado o valor original. Podemos mudar este comportamento adicionando Route::FilterStrict => true
e a rota
rejeitará a URL se o valor não estiver no dicionário.
Além do dicionário de tradução na forma de um array, é possível definir funções de tradução próprias:
use Nette\Routing\Route;
$router->addRoute('<presenter>/<action>/<id>', [
'presenter' => [
Route::Value => 'Home',
Route::FilterIn => function (string $s): string { /* ... */ },
Route::FilterOut => function (string $s): string { /* ... */ },
],
'action' => 'default',
'id' => null,
]);
A função Route::FilterIn
converte entre o parâmetro na URL e a string, que é então passada para
o apresentador, a função FilterOut
assegura a conversão na direção oposta.
Os parâmetros presenter
, action
e module
já possuem filtros predefinidos que convertem
entre o PascalCase resp. estilo camelCase e a caixa kebab utilizada na URL. O valor padrão dos parâmetros já está escrito na
forma transformada, assim, por exemplo, no caso de um apresentador, nós escrevemos <presenter=ProductEdit>
em
vez de <presenter=product-edit>
.
Filtros gerais
Além dos filtros para parâmetros específicos, você também pode definir filtros gerais que recebem uma matriz associativa
de todos os parâmetros que podem modificar de qualquer forma e depois retornar. Os filtros gerais são definidos sob a tecla
null
.
use Nette\Routing\Route;
$router->addRoute('<presenter>/<action>', [
'presenter' => 'Home',
'action' => 'default',
null => [
Route::FilterIn => function (array $params): array { /* ... */ },
Route::FilterOut => function (array $params): array { /* ... */ },
],
]);
Os filtros gerais lhe dão a capacidade de ajustar o comportamento da rota de absolutamente qualquer maneira. Podemos
usá-los, por exemplo, para modificar parâmetros com base em outros parâmetros. Por exemplo, a tradução
<presenter>
e <action>
com base no valor atual do parâmetro <lang>
.
Se um parâmetro tem um filtro personalizado definido e um filtro geral existe ao mesmo tempo, o personalizado
FilterIn
é executado antes do geral e vice-versa geral FilterOut
é executado antes do personalizado.
Assim, dentro do filtro geral estão os valores dos parâmetros presenter
resp. action
escritos em
estilo PascalCase resp. camelCase.
Bandeira OneWay
Rotas unidirecionais são usadas para preservar a funcionalidade de URLs antigas que a aplicação não gera mais, mas ainda
assim aceita. Nós as sinalizamos com OneWay
:
// URL antigo /product-info?id=123
$router->addRoute('product-info', 'Product:detail', $router::ONE_WAY);
// novo URL /produto/123
$router->addRoute('product/<id>', 'Product:detail');
Ao acessar a antiga URL, o apresentador redireciona automaticamente para a nova URL para que os mecanismos de busca não indexem essas páginas duas vezes (ver SEO e canonização).
Roteamento dinâmico com retornos de chamada
O roteamento dinâmico com retornos de chamada permite atribuir diretamente funções (retornos de chamada) às rotas, que serão executadas quando o caminho especificado for visitado. Esse recurso flexível permite a criação rápida e eficiente de vários pontos de extremidade para o seu aplicativo:
$router->addRoute('test', function () {
echo 'You are at the /test address';
});
Você também pode definir parâmetros na máscara, que serão automaticamente passados para o callback:
$router->addRoute('<lang cs|en>', function (string $lang) {
echo match ($lang) {
'cs' => 'Welcome to the Czech version of our website!',
'en' => 'Welcome to the English version of our website!',
};
});
Módulos
Se temos mais rotas que pertencem a um módulo, podemos usar
withModule()
para agrupá-las:
$router = new RouteList;
$router->withModule('Forum') // os seguintes roteadores fazem parte do módulo Forum
->addRoute('rss', 'Feed:rss') // apresentador é Forum:Feed
->addRoute('<presenter>/<action>')
->withModule('Admin') // os seguintes roteadores fazem parte do módulo Forum:Admin
->addRoute('sign:in', 'Sign:in');
Uma alternativa é usar o parâmetro module
:
// Gestão de URLs/ mapas de default para o apresentador Admin:Dashboard
$router->addRoute('manage/<presenter>/<action>', [
'module' => 'Admin',
]);
Subdomínios
As coleções de rotas podem ser agrupadas por subdomínios:
$router = new RouteList;
$router->withDomain('example.com')
->addRoute('rss', 'Feed:rss')
->addRoute('<presenter>/<action>');
Você também pode usar wildcards em seu nome de domínio:
$router = new RouteList;
$router->withDomain('example.%tld%')
// ...
Prefixo do caminho
As coleções de rotas podem ser agrupadas por caminho na URL:
$router = new RouteList;
$router->withPath('eshop')
->addRoute('rss', 'Feed:rss') // combina URL /eshop/rss
->addRoute('<presenter>/<action>'); // combina URL /eshop/<presenter>/<action>
Combinações
O uso acima pode ser combinado:
$router = (new RouteList)
->withDomain('admin.example.com')
->withModule('Admin')
->addRoute(/* ... */)
->addRoute(/* ... */)
->end()
->withModule('Images')
->addRoute(/* ... */)
->end()
->end()
->withDomain('example.com')
->withPath('export')
->addRoute(/* ... */)
// ...
Parâmetros de consulta
As máscaras também podem conter parâmetros de consulta (parâmetros após o ponto de interrogação na URL). Elas não podem definir uma expressão de validação, mas podem alterar o nome sob o qual são passadas ao apresentador:
// usar o parâmetro de consulta 'cat' como uma 'categoriaId' na aplicação
$router->addRoute('product ? id=<productId> & cat=<categoryId>', /* ... */);
Parâmetros Foo
Estamos indo mais fundo agora. Os parâmetros Foo são basicamente parâmetros anônimos que permitem corresponder a uma
expressão regular. O seguinte roteiro corresponde a /index
, /index.html
, /index.htm
e
/index.php
:
$router->addRoute('index<? \.html?|\.php|>', /* ... */);
Também é possível definir explicitamente uma cadeia que será usada para a geração de URLs. A cadeia deve ser colocada
diretamente após o ponto de interrogação. A seguinte rota é semelhante à anterior, mas gera /index.html
ao
invés de /index
porque a string .html
é definida como um “valor gerado”.
$router->addRoute('index<?.html \.html?|\.php|>', /* ... */);
Integração
A fim de conectar nosso roteador à aplicação, devemos informar o recipiente DI sobre isso. A maneira mais fácil é
preparar a fábrica que irá construir o objeto roteador e dizer à configuração do contêiner para usá-lo. Portanto, digamos
que escrevemos um método para este fim App\Core\RouterFactory::createRouter()
:
namespace App\Core;
use Nette\Application\Routers\RouteList;
class RouterFactory
{
public static function createRouter(): RouteList
{
$router = new RouteList;
$router->addRoute(/* ... */);
return $router;
}
}
Em seguida, escrevemos em configuração:
services:
- App\Core\RouterFactory::createRouter
Quaisquer dependências, tais como uma conexão de banco de dados, etc., são passadas para o método de fábrica como seus parâmetros usando a fiação automática:
public static function createRouter(Nette\Database\Connection $db): RouteList
{
// ...
}
SimpleRouter
Um roteador muito mais simples do que a coleta de rotas é o SimpleRouter. Ele pode ser usado
quando não há necessidade de um formato URL específico, quando mod_rewrite
(ou alternativas) não está
disponível ou quando simplesmente não queremos nos preocupar com URLs de fácil utilização ainda.
Gera endereços de forma aproximada:
http://example.com/?presenter=Product&action=detail&id=123
O parâmetro do construtor SimpleRouter
é um apresentador e ação padrão, ou seja, ação a ser executada se
abrirmos, por exemplo, http://example.com/
sem parâmetros adicionais.
// default para o apresentador 'Home' e ação 'default
$router = new Nette\Application\Routers\SimpleRouter('Home:default');
Recomendamos definir o SimpleRouter diretamente na configuração:
services:
- Nette\Application\Routers\SimpleRouter('Home:default')
SEO e Canonização
A estrutura aumenta o SEO (Search Engine Optimization) ao evitar a duplicação de conteúdo em diferentes URLs. Se vários
endereços forem ligados a um mesmo destino, por exemplo /index
e /index.html
, a estrutura determina
o primeiro como primário (canônico) e redireciona os outros para ele usando o código HTTP 301. Graças a isso, os mecanismos
de busca não indexarão páginas duas vezes e não quebrarão sua classificação de páginas. .
Este processo é chamado de canonização. A URL canônica é aquela gerada pelo roteador, ou seja, pela primeira rota de correspondência na coleta sem a bandeira OneWay. Portanto, na coleção, listamos primeiro caminhos primários.
A canonização é realizada pelo apresentador, mais no capítulo canonização.
HTTPS
Para utilizar o protocolo HTTPS, é necessário ativá-lo na hospedagem e configurar o servidor.
O redirecionamento de todo o site para HTTPS deve ser feito no nível do servidor, por exemplo, usando o arquivo .htaccess no diretório raiz de nossa aplicação, com o código HTTP 301. As configurações podem ser diferentes dependendo da hospedagem e se parece com isto:
<IfModule mod_rewrite.c>
RewriteEngine On
...
RewriteCond %{HTTPS} off
RewriteRule .* https://%{HTTP_HOST}%{REQUEST_URI} [L,R=301]
...
</IfModule>
O roteador gera uma URL com o mesmo protocolo que a página foi carregada, portanto, não há necessidade de definir mais nada.
Entretanto, se excepcionalmente precisarmos de rotas diferentes para operar sob protocolos diferentes, a colocaremos na máscara da rota:
// Irá gerar um endereço HTTP
$router->addRoute('http://%host%/<presenter>/<action>', /* ... */);
// Irá gerar um endereço HTTPS
$router->addRoute('https://%host%/<presenter>/<action>', /* ... */);
Roteador de depuração
A barra de roteamento exibida na Tracy Bar é uma ferramenta útil que exibe uma lista de rotas e também os parâmetros que o roteador obteve da URL.
A barra verde com o símbolo ✓ representa a rota que corresponde à URL atual, as barras azuis com símbolos ≈ indicam as rotas que também corresponderiam à URL se o verde não as ultrapassasse. Vemos o apresentador e a ação atual mais adiante.
Ao mesmo tempo, se houver um redirecionamento inesperado devido à canonicalização, é útil olhar na barra redireto para ver como o roteador entendeu originalmente a URL e porque ela foi redirecionada.
Ao depurar o roteador, recomenda-se abrir as Ferramentas do Desenvolvedor no navegador (Ctrl+Shift+I ou Cmd+Option+I) e desabilitar o cache no painel de Rede para que os redirecionamentos não sejam armazenados nele.
Desempenho
O número de rotas afeta a velocidade do roteador. Seu número não deve certamente exceder algumas dúzias. Se seu site tem uma estrutura URL excessivamente complicada, você pode escrever um roteador personalizado.
Se o roteador não tem dependências, como em um banco de dados, e sua fábrica não tem argumentos, podemos serializar sua forma compilada diretamente em um container DI e assim tornar a aplicação um pouco mais rápida.
routing:
cache: true
Roteador personalizado
As linhas a seguir são destinadas a usuários muito avançados. Você pode criar seu próprio roteador e, naturalmente, adicioná-lo à sua coleção de rotas. O roteador é uma implementação da interface Nette\Routing\Router com dois métodos:
use Nette\Http\IRequest as HttpRequest;
use Nette\Http\UrlScript;
class MyRouter implements Nette\Routing\Router
{
public function match(HttpRequest $httpRequest): ?array
{
// ...
}
public function constructUrl(array $params, UrlScript $refUrl): ?string
{
// ...
}
}
O método match
processa o atual $httpRequest, do qual não
apenas a URL, mas também cabeçalhos etc. podem ser recuperados, em uma matriz contendo o nome do apresentador e seus
parâmetros. Se não puder processar o pedido, ele retorna nulo. Ao processar o pedido, devemos retornar pelo menos
o apresentador e a ação. O nome do apresentador é completo e inclui quaisquer módulos:
[
'presenter' => 'Front:Home',
'action' => 'default',
]
O método constructUrl
, por outro lado, gera uma URL absoluta a partir da matriz de parâmetros. Ele pode
utilizar as informações do parâmetro $refUrl
, que é a URL atual.
Para adicionar roteador personalizado à coleta de rotas, use add()
:
$router = new Nette\Application\Routers\RouteList;
$router->add($myRouter);
$router->addRoute(/* ... */);
// ...
Uso separado
Por uso separado, entendemos o uso das capacidades do roteador em uma aplicação que não utiliza Nette Application e apresentadores. Quase tudo o que mostramos neste capítulo se aplica a ela, com as seguintes diferenças:
- para coletas de rotas usamos classe Nette\Routing\RouteList
- como uma classe de roteador simples Nette\Routing\SimpleRouter
- porque não há par
Presenter:action
, usamos a notação avançada
Assim, mais uma vez, criaremos um método que construirá um roteador, por exemplo:
namespace App\Core;
use Nette\Routing\RouteList;
class RouterFactory
{
public static function createRouter(): RouteList
{
$router = new RouteList;
$router->addRoute('rss.xml', [
'controller' => 'RssFeedController',
]);
$router->addRoute('article/<id \d+>', [
'controller' => 'ArticleController',
]);
// ...
return $router;
}
}
Se você usar um recipiente DI, que recomendamos, adicione novamente o método à configuração e, em seguida, obtenha o roteador junto com a solicitação HTTP do recipiente:
$router = $container->getByType(Nette\Routing\Router::class);
$httpRequest = $container->getByType(Nette\Http\IRequest::class);
Ou criaremos objetos diretamente:
$router = App\Core\RouterFactory::createRouter();
$httpRequest = (new Nette\Http\RequestFactory)->fromGlobals();
Agora temos que deixar o roteador trabalhar:
$params = $router->match($httpRequest);
if ($params === null) {
// não foi encontrada nenhuma rota correspondente, enviaremos um erro 404
exit;
}
// nós processamos os parâmetros recebidos
$controller = $params['controller'];
// ...
E vice-versa, usaremos o roteador para criar o link:
$params = ['controller' => 'ArticleController', 'id' => 123];
$url = $router->constructUrl($params, $httpRequest->getUrl());