Docs›Roteamento›URLs amigáveis e i18n de rotas
Roteamento

URLs amigáveis e i18n de rotas

Slugs em lang/{locale}/routes.php, registro inbound nos 3 locales, MadRoutes::urlFor/toFriendlyUrl e por que o mapa não vai para o client.

O MadRoutes tem duas metades. A de entrada (inbound) registra as rotas no Router do Laravel a partir dos slugs traduzidos; a de saida (outbound) responde a pergunta inversa — "qual e a URL desta classe/metodo?" — e alimenta menu, MadAction, MadResponse e o rewriteFriendlyHrefs do shell. As duas leem o MESMO mapa, entao trocar um slug muda a URL de entrada e a de navegacao de uma vez so.

Onde moram os slugs

Um arquivo por locale, em lang/{locale}/routes.php, no grupo routes:

// lang/pt-BR/routes.php
return [
    'login' => ['slug' => 'login'],
    'users' => ['slug' => 'usuarios', 'new' => 'novo',  'edit' => 'editar'],
];

// lang/en/routes.php
return [
    'login' => ['slug' => 'login'],
    'users' => ['slug' => 'users',    'new' => 'new',   'edit' => 'edit'],
];

// lang/es/routes.php
'users' => ['slug' => 'usuarios', 'new' => 'nuevo', 'edit' => 'editar'],

Cada chave (users) e o primeiro argumento dos metodos de registro:

MadRoutes::resource('users', 'UserList', 'UserForm');
Segmento Chave lida Default se ausente
Caminho da tela routes.{key}.slug nenhum — sem slug a rota nao e registrada
Form de criacao routes.{key}.new novo
Form de edicao routes.{key}.edit editar

Inbound: registrado nos tres locales

MadRoutes registra as rotas de todos os locales suportados (pt-BR, en, es) ao mesmo tempo — nao e "o locale ativo manda". /app/usuarios e /app/users respondem os dois, sempre, o que evita 404 quando alguem troca de idioma com uma URL antiga aberta ou copia um link de um colega em outro idioma. Slugs repetidos entre locales sao deduplicados (registra uma vez so).

Limite o conjunto com locales quando fizer sentido:

MadRoutes::resource('users', 'UserList', 'UserForm', ['locales' => ['pt-BR']]);

Sem entrada de slug, nao existe rota. Uma chave ausente em lang/{locale}/routes.php simplesmente nao registra a rota naquele locale — nao ha fallback silencioso, e a tela devolve 404 ate alguem adicionar o slug. E o mesmo principio do modelo allowlist: nada responde em /app/* sem uma linha declarada.

Outbound: urlFor() usa o locale ATIVO

O mapa de saida (classe → caminho) e montado com o locale ativo do request. Ou seja: o app aceita as tres grafias na entrada, mas os links que ele gera saem no idioma do usuario.

MadRoutes::urlFor('UserList');                      // pt-BR → /app/usuarios
MadRoutes::urlFor('UserForm', 'onEdit', ['id'=>42]); // pt-BR → /app/usuarios/42/editar
MadRoutes::urlFor('LoginForm', 'onLogout', ['static' => 1]);
//                                                  → /app/login/onLogout?static=1

Regras que o urlFor() aplica, nesta ordem:

Situacao Resultado
Existe exposeMethod() para o par classe@metodo O caminho literal declarado (tem precedencia sobre o mapa por classe)
Classe de expose() (rota aceita {method?}) /app/{slug}/{metodo}
Classe de screen() ou list de resource(), com metodo /app/{Classe}/{metodo} — a rota por slug e so GET /{slug}; slug+metodo daria 404
Form de resource() com onEdit ou id /app/{slug}/{id}/{edit} (o param de id sai da query string)
Form de resource() sem id /app/{slug}/{new}
Classe fora do mapa /app/{Classe}[/{metodo}] — so resolve com exposeClass() declarado; senao 404, por design

Parametros que sobram viram query string (http_build_query). Um metodo escrito com parenteses vazios ('onShow()', forma que o studio antigo gravava) e normalizado antes de virar segmento — sem isso viraria onShow%28%29 e a constraint [A-Za-z][A-Za-z0-9_]* do expose() responderia 404.

toFriendlyUrl() e so para URL legada

// CERTO — montar URL nova em codigo:
MadRoutes::urlFor('ClienteList', 'onAprovar');

// ERRADO — construir string legada so para converte-la:
MadRoutes::toFriendlyUrl('index.php?class=ClienteList&method=onAprovar');

toFriendlyUrl() existe para traduzir URLs legadas que ja existem como string em dados (menu.xml, actions assadas no banco): ele faz o parse dos parametros e delega ao urlFor(). Para codigo novo, chame urlFor() direto.

O mapa nao vai para o client

O mapa outbound nao e exportado para o JavaScript — publica-lo seria entregar a enumeracao da superficie inteira do admin. Quem precisa de uma URL no browser recebe ela pronta do server, tipicamente como data-url no elemento:

// PHP
$url = MadRoutes::urlFor('ChangeTenantForm', 'onChange');
// <button data-url="/app/trocar-empresa/onChange">
// JS — POST manual precisa do X-CSRF-TOKEN explicito
await fetch(btn.dataset.url, {
    method: 'POST',
    headers: { 'X-CSRF-TOKEN': document.querySelector('meta[name="csrf-token"]').content },
    body: new FormData(form),
});

Ver CSRF protection para o contrato completo desses POSTs do casco.

loginUrl()

MadRoutes::loginUrl() devolve a rota amigavel do login a partir de routes.login.slug — e o que o mad.auth usa ao negar acesso. Nunca escreva /app/login hardcoded num redirect: o slug e configuravel.

Trocando uma URL na pratica

  1. Edite o slug em lang/{locale}/routes.php (o locale que voce quer mudar).
  2. Nao toque em routes/web.php: a chave (users) nao muda, so o segmento.
  3. Limpe o cache de rota/traducao (php artisan route:clear, php artisan config:clear).
  4. Entrada e navegacao mudam juntas — nao ha lista de links para atualizar.

Proximos passos

  • Rotas publicas — os metodos de registro (screen, expose, exposeMethod, resource, exposeClass, exposeService).
  • Middleware stack — mad.auth, mad.permission, mad.api.
  • CSRF protection — token em forms, MadWire e fetch manual.