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.phpsimplesmente 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
- Edite o slug em
lang/{locale}/routes.php(o locale que voce quer mudar). - Nao toque em
routes/web.php: a chave (users) nao muda, so o segmento. - Limpe o cache de rota/traducao (
php artisan route:clear,php artisan config:clear). - 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.