Aprende Java Aprende Php Aprende C++ Aprende HTML 5 Aprende JavaScript Aprende JSON Aprende MySQL Aprende SQLServer Aprende Visual Basic 6 Aprende PostgreSQL Aprende SQLite Aprende Redis Aprende Kotlin Aprende XML Aprende Linux VSC Aprende Wordpress Aprende Laravel Aprende VueJS Aprende JQuery Aprende Bootstrap Aprende Netbeans Aprende Android
Sigueme en Facebook Sigueme en Twitter Sigueme en Instagram Sigueme en Youtube Sigueme en TikTok Sigueme en Whatsapp
Home / Laravel / Laravel 12 Theming Multi-Dominio: Cómo Servir Múltiples Diseños con un Solo Backend Monolítico

Laravel 12 Theming Multi-Dominio: Cómo Servir Múltiples Diseños con un Solo Backend Monolítico

Por jc mouse jueves, octubre 23, 2025

El tema que trataremos en este post es la implementación de una arquitectura de temas dinámicos (theming) dentro de un proyecto monolítico de Laravel 12+, permitiendo que una única base de código y base de datos sirvan a múltiples interfaces de usuario bajo dominios diferentes.

La clave de esta implementación reside en el uso estratégico de un Middleware personalizado. Este componente interceptara cada petición HTTP y mediante la lectura del host o subdominio solicitado (azul.misitio.com o rojo.misitio.com), determina el tema activo para esa sesión. Una vez identificado el tema, el middleware no solo establece una variable global para el tema, sino que también manipula la configuración interna del Buscador de Vistas de Laravel. Esta manipulación es crítica, ya que le indica a Laravel que priorice las rutas de las vistas específicas del tema activo antes de buscar las vistas estándar.

La eficiencia y flexibilidad del sistema se basan en el principio de «fallback» o mecanismo de reserva. Se establecera una carpeta de vistas default/ que contiene la versión base y completa de todas las plantillas de la aplicación. Ademas se crearan directorios individuales para cada Theme que solo necesitan contener las vistas que se quiera personalizar en su diseño a las del resto. Si un tema específico carece de una plantilla, el sistema de búsqueda de vistas automáticamente recurre a la versión almacenada en la carpeta default/, garantizando que la aplicación nunca falle en renderizar contenido, mientras mantiene el código modular y reduce la duplicación innecesaria.

Manos a la obra.

Se necesita:

  • Un proyecto base de laravel que se llamara «example-theme».
  • Sistema operativo Windows.
  • Se usaran referencias a archivos jquery y bootstrapt para mantener el proyecto sencillo.
  • No se hara uso de base de datos, no es necesario.
  • Se usara el entorno de desarrollo Laragon, el mismo crea automaticamente un dominio virtual que en nuestro ejemplo sera «example-theme.test». Ademas de este dominio, se necesita 2 dominios extras, «azul.test» y «rojo.test», los mismos se crearan más adelante.

Creación de multiples dominios virtuales en Laragon

Laragon está diseñado para que cada proyecto en la carpeta www tenga un solo dominio virtual automático (en nuestro caso es, example-theme.test). Para que un solo proyecto, como «example-theme», responda a múltiples dominios virtuales (azul.test y rojo.test), necesitamos modificar manualmente el archivo de configuración del Virtual Host.

Paso 1. Deshabilitar la Creación Automática de Laragon

Abre la dirección; «C:\laragon\etc\apache2\sites-enabled\» y busca el archivo «auto.example-theme.test.conf» , a continuación renombra este archivo a «example-theme.test.conf», al quitar el prefijo «auto.», le indicas a Laragon que mantenga intacto el contenido de ese archivo y no lo regenere automáticamente.

2. Modificar el Archivo de Configuración del Virtual Host

Abre el archivo «example-theme.test.conf», busca la línea que contiene ServerName y la línea ServerAlias.

Añade los nuevos dominios virtuales que necesitamos (azul.test y rojo.test) a la línea ServerAlias, el archivo deve quedar así.

<VirtualHost *:80> 
    DocumentRoot "C:/laragon/www/example-theme/public"
    # Este es el dominio principal generado por Laragon
    ServerName example-theme.test
    # Nuevos dominios separados por un espacio
    ServerAlias azul.test rojo.test
    <Directory "C:/laragon/www/example-theme/public">
        AllowOverride All
        Require all granted
    </Directory>
</VirtualHost>

3. Modificar el Archivo de Hosts de Windows

Navega a la siguiente ruta «C:\Windows\System32\drivers\etc» y abre el archivo hosts, necesitaras permisos de administrador.

Ve hasta el final del archivo y agrega:

127.0.0.1    azul.test
127.0.0.1    rojo.test

Guarda los cambios y cierra.

4. Recargar y Prueba

Guarda los cambios en tu archivo «example-theme.test.conf» y recarga Laragon. Prueba los tres dominios en el navegador, por el momento los tres dominios te mostraran la vista por defecto de Bienvenida de Laravel.

Si todo salio bien hasta aquí, continuemos con el proyecto en Laravel.

Theming Multi-Dominio

Paso 1. Configuración de Temas

Crea un nuevo archivo en config/ el cual se llamara themes.php, este archivo sera el encargado de almacenar los diferentes themes utilizados en proyecto asi como los dominios correspondientes a cada plantilla.

<?php

return [
    // El tema por defecto si no se detecta ningún dominio
    'default' => 'default', 

    // Mapeo de dominios a nombres de temas
    'domains' => [
        'azul.test' => 'azul',
        'rojo.test' => 'rojo',
        'example-theme.test'  => 'default', // Dominio principal        
    ],

    // Ruta base donde se encuentran las vistas de los temas
    'base_path' => resource_path('views/themes'),
];

2. Organización de Vistas y Assets

Crea ls siguiente estructura de archivos:

resources/
└── views/
    └── themes/
        ├── azul/
        │   ├── layouts/
        │   │   └── app.blade.php   // Layout para el tema Azul
        │   └── show.blade.php   
        └── default/
            ├── layouts/
            │   └── app.blade.php   // Layout base con Assets genéricos
            ├── index.blade.php
            └── show.blade.php

No estamos creando un directorio para el «theme rojo», no es un error, explicaremos el porque mas adelante.

show.blade.php para azul:

@extends('layouts.app')

@section('content')
<h1>Show, Tema {{ app('current.theme') }}</h1>
Lorem ipsum, dolor sit amet consectetur adipisicing elit. Quos repellat, asperiores necessitatibus ab cumque odio officiis optio aspernatur mollitia! Debitis tempore temporibus doloribus ab velit tenetur quia deleniti hic fuga.
@endsection

show.blade.php para default:

@extends('layouts.app')

@section('content')
<h1>Show default, Tema {{ app('current.theme') }}</h1>
@endsection

index.blade.php para default

@extends('layouts.app')

@section('content')
<h1>Index, Tema {{ app('current.theme') }}</h1>
plantilla común para todos los themes
@endsection

3. Organización de Assets

Crea los siguientes archivos:

public/
└── assets/
    ├── azul/
    │   ├── css/
    │   │   └── style.css       // Estilos personalizados de Azul
    │   └── js/
    │       └── custom.js
    ├── rojo/
    │   ├── css/
    │   │   └── style.css       // Estilos personalizados de Rojo
    │   └── js/
    │       └── custom.js
    └── default/
        ├── css/
        │   ├── bootstrap.min.css // Bootstrap de base
        │   └── style.css
        └── js/
            ├── custom.js
            ├── jquery.min.js //librerias comunes
            └── bootstrap.bundle.min.js //librerias comunes

Aqui si creamos un CSS para el theme rojo.

style.css para azul:

body{
    background-color:#001cc3;
    color: #ffffff;
}

style.css para rojo:

body{
    background-color:#c30000;
    color: #ffffff;
}

custom.js para azul:

window.alert("¡Theme azul se ha cargado!");

4. Creación del Middleware de Detección de Temas (ThemeMiddleware)

Este middleware leerá el host, determinará el tema y ajustará la ruta de las vistas y compartirá el nombre del tema.

<?php

namespace App\Http\Middleware;

use Closure;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\View;
use Illuminate\Support\Facades\Config;

class ThemeMiddleware
{
    /**
     * Handle an incoming request.
     *
     * @param  \Closure(\Illuminate\Http\Request): (\Symfony\Component\HttpFoundation\Response)  $next
     */
    public function handle(Request $request, Closure $next)
    {
        
        $host = $request->getHost();
        $themesConfig = Config::get('themes');

        //Determinar el nombre del tema
        $themeName = $themesConfig['domains'][$host] ?? $themesConfig['default'];
        //dd($host, $themeName); //depuración
        
        //Establecer el nombre del tema en el contenedor de servicios
        app()->instance('current.theme', $themeName);

        //Configurar la ruta de las vistas
        $themePath = "themes/{$themeName}";
        
        //Priorizar la carpeta del tema activo
        View::addLocation(resource_path("views/{$themePath}"));
        
        //Agregar la carpeta por defecto como fallback (Respaldo)
        if ($themeName !== $themesConfig['default']) {
            View::addLocation(resource_path("views/themes/{$themesConfig['default']}"));
        }        
        return $next($request);
    }
}

5. Registro del Middleware

Los middleware en Laravel 12 se registran en bootstrap/app.php, abre el archivo y edita:

<?php

use Illuminate\Foundation\Application;
use Illuminate\Foundation\Configuration\Exceptions;
use Illuminate\Foundation\Configuration\Middleware;

return Application::configure(basePath: dirname(__DIR__))
    ->withRouting(
        web: __DIR__.'/../routes/web.php',
        commands: __DIR__.'/../routes/console.php',
        health: '/up',
    )
    ->withMiddleware(function (Middleware $middleware): void {
        $middleware->append(\App\Http\Middleware\ThemeMiddleware::class);
    })
    ->withExceptions(function (Exceptions $exceptions): void {
        //
    })->create();

6. Layout base

El layout base (layouts/app.blade.php) usará el nombre del tema compartido para cargar los assets correctos.

Incluye este código en todos los layouts app.blade.php:

<!DOCTYPE html>
<html lang="es">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>{{ config('app.name') }} | {{ app('current.theme') }}</title>

    @php
        $theme = app('current.theme'); // Obtiene theme 'azul', 'rojo' o 'default'
        $basePath = 'assets/default';
        $themePath = "assets/{$theme}";
    @endphp

    <link rel="stylesheet" href="{{ asset("{$basePath}/css/bootstrap.min.css") }}">
    
    <link rel="stylesheet" href="{{ asset("{$themePath}/css/style.css") }}">

</head>
<body>
    @yield('content') 
    
    <script src="{{ asset("{$basePath}/js/jquery.min.js") }}"></script>
    <script src="{{ asset("{$basePath}/js/bootstrap.bundle.min.js") }}"></script>

    <script src="{{ asset("{$themePath}/js/custom.js") }}"></script>
</body>
</html>

7. Controlador y Rutas

Creamos un controlador llamado DummyController.php con el siguiente código:

<?php

namespace App\Http\Controllers;

use Illuminate\Http\Request;

class DummyController extends Controller
{
    public function index()
    {        
        return view('index');        
    }   
    
    public function show()
    {        
        return view('show');        
    }

}

Y en el archivo routes/web.php modificamos de la siguiente manera:

<?php

use Illuminate\Support\Facades\Route;
use App\Http\Controllers\DummyController;

Route::get('/', function () {
    return view('welcome');
});

Route::get('/index', [DummyController::class, 'index'])->name('dummys.index');
Route::get('/show', [DummyController::class, 'show'])->name('dummys.show');

Funcionamiento Theming Multi-Dominio

Si visitamos azul.test/show , ThemeMiddleware detecta el host y establece el tema activo como azul, ademas como el archivo show.blade.php existe en el directorio azul/, mostrara su contenido y no el de el directorio default/. Incluso mostrará un dialog que se encuentra declarado en el archivo assets/azul/js/custom.js

No asi si visitamos example-theme.test/show, solo mostrara los archivos correspondientes a ese theme.

Ahora bien, recuerdas que no creamos un directorio para el theme «rojo», aunque si declaramos archivo de estilos css para assets/rojo/css/style.css. Pues bien, lo que hace ThemeMiddleware es buscar lso archivos para el theme rojo, pero al no existir, carga el theme por defecto default, no pasa lo mismo con los estilos que si los tenemos declarados.

Similar caso ocurre cuando visitamos azul.test/index o rojo.test/index o example-theme.test/index, el archivo index.blade.php solo esta declarado en el directorio default/, por lo que es el archivo comun para todos los demas themes. Esa es la estrategia de «fallback» (reserva) y es la forma más eficiente de gestionar múltiples temas con una base de código común.

En resumen y para concluir, la implementación de temas dinámicos en Laravel 12+ vía middleware y fallback de vistas ofrece una solución elegante y de bajo acoplamiento para el desafío de ofrecer múltiples interfaces de marca. Este método optimiza el mantenimiento, ya que cualquier cambio en la lógica central o en las vistas base (default/) se propaga instantáneamente a todos los temas que no hayan sobrescrito esa plantilla específica.

Enjoy!

Tags

Artículos similares

KolibriOS Sistema Operativo Open Source

KolibriOS  es un pequeño sistema operativo poderoso, rápido y libre con un núcleo monolítico anticipativo en tiempo real[...]

Formateo de registros en Excel con JExcel

Tenia un problema, me pasaron unos archivos excel con unos cientos de registros (ver imagen más abajo) que exportaron de[...]

Tradukisto: Conversión de dinero en palabras

Tradukisto es una biblioteca para Java 8 creada para convertir números enteros o cantidades de dinero a sus equivalentes[...]

CUF: Código Único de Factura

Este 2019 se implementara en Bolivia un nuevo Sistema de Facturación Electrónica con nuevas características y medidas de[...]

La era de la Inteligencia Artificial

The Age of AI o «La era de la Inteligencia Artificial»,  es una serie de 8 documentales de Youtube Original, presentados[...]

Introducción a SQLite

Android hace uso de la base de datos SQLite para el manejo de registros en las aplicaciones. Según Santa Wikipedia defin[...]