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 y routes/api.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, /features, el centro de ayuda, /locale/{locale}, /logs, /offline y los endpoints de web push |
routes/auth.php |
web |
ninguno | ninguno | Inicio de sesión, registro, restablecer contraseña y verificación, /logout, /stop-acting, /privacy, /terms, y el área de cuenta bajo /account con auth y security |
routes/admin.php |
web, auth, security, role:admin |
admin |
admin. |
El dashboard de administración, los CRUD de Laravel Front, categories/{type}, dev y webhook-events |
routes/app.php |
web, auth, security, y ensure.subscribed cuando la facturación está activa |
app |
app. |
El producto. El kit sólo trae el dashboard en /app |
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 |
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.
| 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, deja en flash los avisos que el layout muestra arriba de la página; hoy, el PIN faltante cuando config('auth.enable_pin') está activo |
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 |
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 resources/views/layouts/app.blade.php; 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
resources/views/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 |
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. 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.
Agrega una entrada a la barra lateral
Cada barra lateral renderiza <x-sidebar-menu>, respaldado por App\View\Components\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.
Para agregar una página a la barra lateral del producto, edita layouts/sidebars/app.blade.php:
<x-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/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 y mientras se envía un formulario con wire:submit. 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:
<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.
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. <x-language-switcher> renderiza un enlace por idioma con locale_url() y sólo para invitados. Ve Idiomas y traducciones.