Rutas, layouts y middleware
Dónde se carga cada archivo de rutas, qué corre en cada petición, y cómo encajan páginas, barras laterales y la capa de carga.
Tu primera página necesita tres decisiones: en qué archivo de rutas va, en qué layout se renderiza y qué entrada de la barra lateral la abre. Esta guía responde las tres y lista el middleware que ya corre en cada petición para que no lo agregues dos veces.
Archivos de rutas
bootstrap/app.php conecta los archivos. Laravel carga routes/web.php, routes/api.php, routes/console.php y routes/channels.php por su cuenta; el resto se carga en el callback then: con el middleware, prefijo y nombre de abajo.
| Archivo | Middleware | Prefijo | Prefijo de nombre | Qué vive ahí |
|---|---|---|---|---|
routes/web.php |
web |
ninguno | ninguno | La landing en /; las páginas públicas una vez por idioma, como /en/features y /es/caracteristicas, la demo, los complementos públicos y los textos legales, más las direcciones sin idioma que redirigen a ellas; el centro de ayuda bajo /{locale}/help; /sitemap.xml, /robots.txt, /locale/{locale}, /logs, /offline, los endpoints de web push y los de subida directa bajo /media-uploads |
routes/auth.php |
web |
ninguno | ninguno | Inicio de sesión, registro, restablecer contraseña y verificación, /logout, /stop-acting, /privacy y /terms (que redirigen al idioma del lector), y con auth y security: el área de cuenta bajo /account y las páginas del equipo de soporte bajo /admin/support (admin.support.*: la bandeja, cada ticket, chats y assistant), dentro del panel de administración pero fuera de su rol, cada una protegida por su propio interruptor de módulo y su permiso |
routes/admin.php |
web, auth, security, role: con config('app.admin_role') |
admin |
admin. |
El dashboard de administración, los CRUD de Laravel Front, categories/{type}, dev, design, space, las carpetas de medios bajo media y webhook-events |
routes/app.php |
web, auth, security, y ensure.subscribed cuando la facturación está activa |
app |
app. |
El producto. El kit trae el dashboard en /app, la biblioteca de medios en /app/media, la página de espacio en /app/space y categories/{type} |
routes/api.php |
api |
api |
ninguno | POST /api/stripe/webhook, detrás de webhook y log.webhook:stripe |
routes/console.php |
Closures de Artisan y la programación; ve Colas y trabajo programado | |||
routes/channels.php |
Los canales privados de broadcasting; ve Tiempo real con Reverb |
La verificación de salud responde en /up.
ensure.subscribed se agrega a /app sólo cuando el paquete de facturación está instalado y BILLING_CORE_ENABLED y BILLING_USER_ENABLED no son false; sin facturación el alias no existe y una persona con sesión llega a /app sin suscripción. Ve Activa planes y facturación.
Agrega tus propias rutas
Las páginas del producto van en routes/app.php; heredan el prefijo /app y el nombre app.. Registra un componente Livewire como página completa:
use App\Livewire\App;
Route::livewire('/orders', App\Orders\Index::class)->name('orders');
Route::livewire('/orders/{order}', App\Orders\Show::class)->name('orders.show');
Eso es /app/orders, con nombre app.orders. config('app.home_route') es /app, el lugar al que /home redirige y en el que aterriza el flujo de inicio de sesión; cámbialo en config/app.php cuando el producto empiece en otro lado. Los CRUD de administración van en routes/admin.php como Route::front('Order'), que registra cada ruta del recurso App\Front\Resources\Order; ve El panel de administración.
Middleware en cada petición web
bootstrap/app.php agrega seis clases al grupo web, en este orden, y una séptima cuando el paquete de facturación está activo.
| Middleware | Qué hace |
|---|---|
LocaleMiddleware |
Define el idioma: el locale de la cuenta con sesión, si no la elección en sesión del visitante, si no el primer idioma del navegador que la app publica, si no el de respaldo |
RememberLastUrl |
Guarda en la sesión la URL de cada página GET normal como last_non_livewire_url, que lastUrl() lee y que los reportes de error incluyen |
CaptureReferralCode |
En un GET normal, recuerda un código de referido del query string o lo restaura desde la cookie; ve Referidos |
SystemValidations |
Para una persona con sesión, prepara los avisos que el layout muestra arriba de la página: el PIN faltante cuando config('auth.enable_pin') está activo y, mientras la facturación está activa, un pago fallido, un saldo pendiente o una cuenta sin plan |
TrackingMiddleware |
Cuando config('features.tracking_enabled') está activo, abre una sesión de seguimiento de visitantes en el primer GET normal o cuando llegan fbclid o utm_campaign |
UpdateLastLoggedAt |
Marca last_logged_at en la cuenta con sesión una vez al día |
EnsureHasCountry |
Sólo con la facturación activa, y sólo mientras los planes están vivos: a una cuenta con sesión sin país se le da el detectado, y cuando no se puede detectar ninguno, cada navegación de página fuera del panel de administración, del perfil y de las páginas de cerrar sesión y verificación la manda a su perfil con un aviso para que elija uno |
Todos se saltan las peticiones de actualización de Livewire, así que corren una vez por página, no una vez por interacción.
trustProxies(at: '*') confía en cualquier proxy, así que el esquema y la IP del cliente salen de los encabezados reenviados por el balanceador frente a la app.
Alias
| Alias | Clase |
|---|---|
security |
App\Http\Middleware\Security: manda a /password/request a quien debe cambiar su contraseña y cierra la sesión de una cuenta bloqueada |
role, permission, role_or_permission |
El middleware de permisos de Spatie; ve Roles y permisos |
log.webhook |
App\Http\Middleware\LogWebhookEvent: guarda cada webhook entrante, su respuesta y su error como una fila; ve Rastros de auditoría y eventos de webhook |
webhook |
VerifyWebhookSignature de Cashier |
ensure.subscribed |
EnsureHasSubscription del paquete de facturación, presente sólo cuando la facturación está activa |
App\Http\Middleware\IsPWABuilder está atado sólo a /: cuando APP_PWA está activo y la petición viene de la app móvil empaquetada, redirige a /app en lugar de mostrar la landing.
Convenciones de Livewire
config/livewire.php fija dónde va cada cosa.
| Ajuste | Valor | Significado |
|---|---|---|
class_namespace |
App\Livewire |
Los componentes son clases bajo app/Livewire/, agrupadas como Admin, App, Auth, Shared y Web |
view_path |
resources/views/livewire |
Sus vistas reflejan ese árbol |
component_layout |
layouts::app |
El layout en el que se renderiza un componente de página completa salvo que llame ->layout() |
component_namespaces |
layouts, pages |
layouts::app es layouts/app.blade.php, buscado por las capas de diseño igual que layouts.app; pages:: apunta a resources/views/pages/ |
make_command.type |
class |
php artisan make:livewire crea una clase más una vista, no un componente de un solo archivo |
legacy_model_binding |
true |
wire:model="post.title" liga directo a un atributo del modelo |
navigate.progress_bar_color |
#2299dd |
El color de la barra delgada que se muestra durante wire:navigate |
Layouts
Cada layout vive en una carpeta layouts/ dentro de una carpeta de diseño y se busca por la misma cadena que los componentes de diseño: primero resources/views/components/tu-proyecto/layouts/, al final la genérica resources/views/components/project/layouts/, y gana la primera que lo tiene. Se sigue llamando por su nombre, layouts.app o layouts.sidebars.admin; Hazlo tuyo explica cómo cambiar uno en tu carpeta.
layouts/base.blade.php es el cascarón HTML: el <head> con el título, el favicon, los assets de Vite y los scripts de WireUI, luego <x-notifications>, <x-dialog>, la capa de carga, la sección content-base y el anfitrión de modales. Los demás layouts lo extienden:
| Layout | Lo usan |
|---|---|
layouts/app.blade.php |
Cada página con sesión: barra lateral, barra superior con notificaciones, avisos y el menú de la cuenta, migas de pan, mensajes flash y los avisos del sistema |
layouts/auth.blade.php |
Inicio de sesión, registro y los demás formularios de invitado, sin adornos |
layouts/web.blade.php |
La landing y las páginas públicas, con la navegación de marketing |
layouts/help.blade.php |
El centro de ayuda |
Los layouts público y de ayuda ponen en la pestaña del navegador el nombre de la página: pasa title a layouts.web o layouts.help y la pestaña dice Título - Nombre de la app. Sin él, el layout público muestra solo el nombre de la app y el de ayuda Centro de ayuda.
Una dirección que no existe muestra resources/views/errors/404.blade.php dentro de layouts.auth: un No encontramos esa página traducido con un botón Volver al inicio. El botón lleva a un visitante a /, a un administrador al panel de administración y a cualquier otro usuario con sesión al panel de la app. Cambia ese archivo para cambiar la página; un 404 que tu código lance con abort(404) también la muestra.
El layout de la app elige su barra lateral por el primer segmento de la URL: layouts/sidebars/app.blade.php bajo /app, admin.blade.php bajo /admin, account.blade.php bajo /account; una sección para la que ninguna carpeta tiene menú no muestra ninguno. Un componente renderiza migas de pan pasándolas al layout:
public function render()
{
return view('livewire.app.orders.show')->layout('layouts.app', [
'breadcrumb' => [
['label' => __('Orders'), 'url' => '/app/orders'],
['label' => $this->order->number],
],
]);
}
layouts/partials/breadcrumbs.blade.php imprime un enlace de inicio a la raíz de la sección y una entrada por elemento; el último es la página actual y no lleva URL. Una pantalla cuyo camino debe empezar en otro lado le pasa al layout un title para ese primer enlace y una url para él, como hacen las pantallas de soporte bajo /admin/support para que su camino empiece en su propia sección.
Agrega una entrada a la barra lateral
Cada barra lateral renderiza <x-design::sidebar-menu>, respaldado por App\Classes\SidebarMenu. Fusiona los elementos que escribes en el archivo Blade con los recursos de Laravel Front de la sección que responden true a showOnMenu() y pasan la política viewAny para la persona con sesión, ordena todo por order y agrupa por menu_group. Los elementos sin grupo forman el bloque sin encabezado de arriba. resources en false deja fuera los recursos de Laravel Front, como hace la barra de administración con quien no tiene el rol de administrador, y un elemento puede traer su propio active, que gana sobre el que se calcula por su URL, para una entrada cuya dirección es prefijo de la de otra.
Para agregar una página a la barra lateral del producto, edita layouts/sidebars/app.blade.php en tu carpeta de diseño, copiado del genérico la primera vez:
<x-design::sidebar-menu
:items="[
['name' => 'Dashboard', 'url' => '/app', 'icon' => 'home', 'exact' => true, 'order' => 1],
['name' => 'Orders', 'url' => '/app/orders', 'icon' => 'shopping-bag', 'order' => 2],
['name' => 'Reports', 'url' => '/app/reports', 'icon' => 'chart-bar', 'menu_group' => 'Analytics', 'show' => auth()->user()->can('viewAny', Report::class)],
]"
:groups="[
['name' => 'Analytics', 'order' => 1],
]"
/>
| Llave | Significado |
|---|---|
name |
Pasa por __(), así que agrégalo a lang/ |
url |
Se marca activo cuando la URL actual es esa o empieza con ella, salvo que exact sea true |
icon |
Un nombre de Heroicon |
order |
Posición; los elementos sin ella van al final |
show |
Oculta el elemento cuando es false |
menu_group |
Pone el elemento bajo un encabezado plegable; groups ordena los encabezados |
Un recurso de Front declara su propio menu_group, menu_order e icon, así que un CRUD aparece en la barra lateral de administración sin tocar el archivo Blade.
La capa de carga
resources/views/components/project/loading-overlay.blade.php es un spinner de pantalla completa montado una vez en el layout base y controlado por resources/js/app.js. Se muestra solo en la navegación con wire:navigate, en clics a enlaces normales que salen de la página, cada vez que la página se descarga (una recarga completa, un formulario enviado a la manera clásica; los eventos beforeunload y pagehide del navegador) y mientras se envía un formulario con wire:submit. Las navegaciones también dibujan una barra de progreso delgada arriba. Después de 3 segundos agrega un mensaje y un botón Recargar, para que una petición atorada siempre tenga salida.
Para un botón que no es el envío de un formulario, actívala con el atributo spinner en el elemento que lleva wire:click. El atributo sólo cuenta en un elemento que tiene wire:click o wire:target él mismo; en un contenedor alrededor del botón no hace nada:
<x-button wire:click="retryPayment" spinner :label="__('Retry payment')" />
Para una subida de archivo, pon spinner en el <input type="file"> mismo, no en el botón que abre el selector, porque la petición arranca en el change del input; la capa se queda mientras el archivo se sube.
No agregues spinner a campos wire:model.live, filtros, pestañas ni búsquedas: la capa bloquea toda la pantalla y esas interacciones deben sentirse instantáneas.
El cambio de idioma
GET /locale/{locale}, con nombre locale, revisa el código contra las llaves de config('app.languages') y responde 404 a cualquier otro. Para un visitante guarda la elección en la sesión, que LocaleMiddleware lee en la siguiente petición; para una persona con sesión no escribe nada, porque su idioma sale del perfil. En ambos casos redirige de regreso, o a la misma página pública en el idioma elegido cuando el enlace nombra una. <x-language-switcher> renderiza un enlace por idioma con locale_url(), sólo para invitados fuera del centro de ayuda; en el centro de ayuda se muestra a todos y enlaza directo la copia de la página en cada idioma. Ve Idiomas y traducciones.