Una extensión para usar Laravel en una configuración multidominio

Este paquete permite que una única instalación de Laravel funcione con múltiples dominios HTTP.
Hay muchos casos en los que diferentes clientes utilizan la misma aplicación en términos de código pero no en términos de base de datos, almacenamiento y configuración.
Este paquete ofrece una forma muy sencilla de obtener un archivo env específico, una ruta de almacenamiento específica y una base de datos específica para cada cliente.
| Laravel | Multidominio |
|---|---|
| 11.x | 11.x |
| 10.x | 10.x |
| 9.x | 5.x |
| 8.x | 4.x |
| 7.x | 3.x |
| 6.x | 2.x |
| 5.8.x | 1.4.x |
| 5.7.x | 1.3.x |
| 5.6.x | 1.2.x |
| 5.5.x | 1.1.x |
Versiones v1.1.x:
Hasta la fecha, las versiones v1.1.6+, v1.2.x, v1.3.x, v1.4.x, v2.x y v3.x son funcionalmente equivalentes. Las versiones se han separado para poder ejecutar pruebas de integración con la versión correspondiente del framework Laravel.
Sin embargo, con el lanzamiento de Laravel 8, las versiones v1.1.14, v1.2.8, v1.3.8 y v1.4.8 son las últimas versiones que incluyen nuevas características para las versiones correspondientes de Laravel 5.x (la compatibilidad con corrección de errores aún está activa para esas versiones) . ACTUALIZACIÓN del 13 de febrero de 2021 : algunas de las últimas funciones para las versiones v1.1+ aún están en curso :)
v1.0 requiere Laravel 5.1, 5.2, 5.3 y 5.4 (ya no se mantiene ni se prueba en comparación con laravel 5.4, sin embargo, el uso del paquete es el mismo que para 1.1)
2023-02-20 ACTUALIZACIÓN : Desde Laravel 10.x en adelante, las versiones del paquete siguen la misma numeración.
Agregue gecche/laravel-multidomain como requisito a compositor.json:
{
"require" : {
"gecche/laravel-multidomain" : "11.*"
}
}Actualice sus paquetes con Composer Update o instálelos con Composer Install.
También puede agregar el paquete usando composer require gecche/laravel-multidomain y luego especificar la versión que desea.
Este paquete necesita anular la detección del dominio HTTP en un conjunto mínimo de funciones principales de Laravel al comienzo del proceso de arranque para obtener el archivo de entorno específico. Entonces, este paquete necesita algunos pasos de configuración más que la mayoría de los paquetes de Laravel.
Pasos de instalación:
bootstrap/app.php . //use Illuminate F oundation A pplication
use Gecche Multidomain Foundation ApplicationQueueServiceProvider con el extendido en el archivo config/app.php de la siguiente manera: ' providers ' => Illuminate Support ServiceProvider:: defaultProviders ()-> merge ([
// Package Service Providers . . .
])-> replace ([
Illuminate Queue QueueServiceProvider::class => Gecche Multidomain Queue QueueServiceProvider::class,
])-> merge ([
// Added Service Providers ( Do not remove this line ) . . .
])-> toArray (), Tenga en cuenta que si cambió el archivo config/app.php por otros motivos, probablemente ya exista la entrada providers anterior en ese archivo y la única línea importante es la que reemplaza QueueServiceProvider .
php artisan vendor:publish
(Este paquete utiliza la función de descubrimiento).
Siguiendo los pasos anteriores, su aplicación conocerá el dominio HTTP en el que se está ejecutando, tanto para solicitudes HTTP como CLI, incluido el soporte de cola.
NOTA: en Laravel 11 la instalación es más sencilla que antes: si utilizas una versión anterior de Laravel, consulta en la documentación los pasos de instalación.
El paquete es compatible con Horizon, gracias a las contribuciones de la comunidad. Si necesita utilizar este paquete junto con Horizon, debe seguir otros dos pasos de instalación:
Instale Laravel Horizon como de costumbre
Reemplace la importación de Laravel Horizon en la parte superior del archivo app/Providers/HorizonServiceProvider.php.
//use Laravel H orizon H orizonApplicationServiceProvider ;
use Gecche Multidomain Horizon HorizonApplicationServiceProvider ;Este paquete agrega tres comandos para administrar los dominios HTTP de su aplicación:
domain.add comando artesanal El comando principal es el domain:add comando que toma como argumento el nombre del dominio HTTP para agregar a la aplicación. Supongamos que tenemos dos dominios, site1.com y site2.com , que comparten el mismo código.
Simplemente hacemos:
php artisan domain:add site1.com
y
php artisan domain:add site2.com
Estos comandos crean dos nuevos archivos de entorno, .env.site1.com y .env.site2.com , en los que puede colocar la configuración específica para cada sitio (por ejemplo, configuración de bases de datos, configuración de caché y otras configuraciones, como se suele encontrar en un entorno archivo).
El comando también agrega una entrada en la clave domains en el archivo config/domains.php .
Además, se crean dos carpetas nuevas, storage/site1_com/ y storage/site2_com/ . Tienen la misma estructura de carpetas que el almacenamiento principal.
Las personalizaciones de esta subestructura storage deben coincidir con los valores del archivo config/domain.php .
domain.remove comando artesanal El comando domain:remove elimina el dominio HTTP especificado de la aplicación eliminando su archivo de entorno. P.ej:
php artisan domain:remove site2.com
Agregar la opción force eliminará la carpeta de almacenamiento del dominio.
El comando también elimina la entrada apropiada de la clave domains en el archivo config/domains.php .
domain.update_env El comando domain:update_env pasa una matriz de datos codificada en json para actualizar uno o todos los archivos del entorno. Estos valores se agregarán al final del .env correspondiente.
Actualice un archivo de entorno de dominio único agregando el argumento domain .
Cuando el argumento domain está ausente, el comando actualiza todos los archivos del entorno, incluido el estándar .env .
La lista de dominios que se actualizarán se mantiene en el archivo de configuración domain.php .
P.ej:
php artisan domain:update_env --domain_values='{"TOM_DRIVER":"TOMMY"}'
agregará la línea TOM_DRIVER=TOMMY a todos los archivos del entorno del dominio.
domain.list El comando domain:list enumera los dominios actualmente instalados, con su archivo .env y su directorio de ruta de almacenamiento.
La lista se mantiene en la clave domains del archivo de configuración config/domain.php .
Esta lista se actualiza automáticamente en cada domain:add y domain:remove comandos ejecutados.
config:cacheEl comando artesanal config:cache se puede utilizar con este paquete de la misma manera que cualquier otro comando artesanal.
Tenga en cuenta que este comando generará un archivo config.php para cada dominio bajo el cual se haya ejecutado el comando. es decir, el comando
php artisan config:cache --domain=site2.com
generará el archivo
config-site2_com.php
En tiempo de ejecución, el dominio HTTP actual se mantiene en el contenedor laravel y se puede acceder a él mediante su método domain() agregado por este paquete.
Hay disponible un método domainList() . Devuelve una matriz asociativa que contiene la información de los dominios instalados, similar al comando domain.list anterior.
P.ej
[
site1.com => [
'storage_path' => <LARAVEL-STORAGE-PATH>/site1_com,
'env' => '.env.site1.com'
]
]
Para cada solicitud HTTP recibida por la aplicación, se carga el archivo de entorno específico y se utiliza la carpeta de almacenamiento específica.
Si no se encuentra ningún archivo de entorno específico y/o carpeta de almacenamiento, se utiliza el estándar.
La detección del dominio HTTP correcto se realiza utilizando la variable PHP $_SERVER['SERVER_NAME'] .
NOTA IMPORTANTE: en algunos entornos de ejecución no se crea una instancia de $_SERVER['SERVER_NAME'], por lo que este paquete no funciona correctamente hasta que personalice la detección de dominios HTTP como se describe a continuación.
A partir de la versión 1.1.15, la detección de dominios HTTP se puede personalizar pasando un Closure como la entrada domain_detection_function_web del nuevo argumento domainParams del constructor de Application . En el siguiente ejemplo, la detección del dominio HTTP se basa en $_SERVER['HTTP_HOST'] en lugar de $_SERVER['SERVER_NAME'] .
//use Illuminate F oundation A pplication ;
use Gecche Multidomain Foundation Application ;
use Illuminate Foundation Configuration Exceptions ;
use Illuminate Foundation Configuration Middleware ;
$ environmentPath = null ;
$ domainParams = [
' domain_detection_function_web ' => function () {
return Illuminate Support Arr:: get ( $ _SERVER , ' HTTP_HOST ' );
}
];
return Application:: configure (basePath: dirname ( __DIR__ ),
environmentPath: $ environmentPath ,
domainParams: $ domainParams )
-> withRouting (
web: __DIR__ . ' /../routes/web.php ' ,
commands: __DIR__ . ' /../routes/console.php ' ,
health: ' /up ' ,
)
-> withMiddleware ( function ( Middleware $ middleware ) {
//
})
-> withExceptions ( function ( Exceptions $ exceptions ) {
//
})-> create (); Para distinguir entre dominios, cada comando artesanal acepta una nueva opción: domain . P.ej:
php artisan list --domain=site1.com
El comando utilizará la configuración de dominio correspondiente.
Los comandos artesanales queue:work y queue:listen se han actualizado para aceptar una nueva opción domain .
php artisan queue:work --domain=site1.com
Como de costumbre, el comando anterior utilizará la configuración de dominio correspondiente.
Tenga en cuenta que si, por ejemplo, está utilizando el controlador database y tiene dos dominios que comparten la misma base de datos, debe usar dos colas distintas si desea administrar los trabajos de cada dominio por separado.
Por ejemplo, podrías:
QUEUE_DEFAULT=default1 para site1.com y QUEUE_DEFAULT=default2 para site2.comqueue.php cambiando la cola predeterminada en consecuencia: 'database' => [
'driver' => 'database',
'table' => 'jobs',
'queue' => env('QUEUE_DEFAULT','default'),
'retry_after' => 90,
],
php artisan queue:work --domain=site1.com --queue=default1
y
php artisan queue:work --domain=site1.com --queue=default2
Obviamente, se puede hacer lo mismo con cada uno de los controladores de cola, además del controlador sync .
storage:link Si utiliza el comando storage:link y desea un enlace simbólico distinto para cada dominio, debe crearlos manualmente porque hasta la fecha dicho comando siempre crea un enlace llamado storage y ese nombre está codificado en el comando. Extender el comando storage:link que permite elegir el nombre está fuera del alcance de este paquete (y espero que se haga directamente en futuras versiones de Laravel).
Una forma de obtener múltiples enlaces de almacenamiento podría ser la siguiente. Supongamos que tenemos dos dominios, a saber, site1.com y site2.com con carpetas de almacenamiento asociadas storage/site1_com y storage/site2_com .
ln -s storage/site1_com/app/public public/storage-site1_com
ln -s storage/site2_com/app/public public/storage-site2_com
.env.site1.com y .env.site2.com agregamos una entrada, por ejemplo, para el primer dominio: APP_PUBLIC_STORAGE=-site1_com
filesystems.php cambiamos de la siguiente manera: 'public' => [
'driver' => 'local',
'root' => storage_path('app/public'),
'url' => env('APP_URL').'/storage'.env('APP_PUBLIC_STORAGE'),
'visibility' => 'public',
],
Además, si está utilizando el paquete en una configuración de aplicación de página única (SPA), podría manejar mejor recursos públicos distintos para cada dominio a través de .htaccess o soluciones similares, como lo señala Scaenicus en su solución .htaccess.
A partir de la versión 1.1.11 se agregó un segundo argumento al constructor de la aplicación para elegir la carpeta donde colocar los archivos de entorno: si tienes decenas de dominios, no es muy agradable tener archivos de entorno en la aplicación raíz de Laravel. carpeta.
Entonces, si desea utilizar una carpeta diferente, simplemente agréguela en la parte superior del archivo bootstrap/app.php . por ejemplo, si desea agregar archivos de entorno a la subcarpeta envs , simplemente haga:
//use Illuminate F oundation A pplication ;
use Gecche Multidomain Foundation Application ;
use Illuminate Foundation Configuration Exceptions ;
use Illuminate Foundation Configuration Middleware ;
$ environmentPath = dirname ( __DIR__ ) . DIRECTORY_SEPARATOR . ' envs ' ;
$ domainParams = [];
return Application:: configure (basePath: dirname ( __DIR__ ),
environmentPath: $ environmentPath ,
domainParams: $ domainParams )
-> withRouting (
web: __DIR__ . ' /../routes/web.php ' ,
commands: __DIR__ . ' /../routes/console.php ' ,
health: ' /up ' ,
)
-> withMiddleware ( function ( Middleware $ middleware ) {
//
})
-> withExceptions ( function ( Exceptions $ exceptions ) {
//
})-> create (); Si no especifica el segundo argumento, se asume la carpeta estándar. Tenga en cuenta que si especifica una carpeta, también se debe colocar en ella el archivo .env estándar.
Si intenta ejecutar una página web o un comando de shell bajo un determinado dominio, por ejemplo sub1.site1.com y no existe un archivo de entorno específico para ese dominio, es decir, el archivo .env.sub1.site1.com no existe, el El paquete utilizará el primer archivo de entorno disponible dividiendo el nombre de dominio con puntos. En este ejemplo, el paquete busca el primer archivo de entorno entre los siguientes:
.env.site1.com
.env.com
.env
La misma lógica se aplica también a la carpeta de almacenamiento.
Si en tu entorno haces uso del Scheduler de Laravel, recuerda que también el comando schedule:run debe ejecutarse con la opción de dominio. Por lo tanto, debes iniciar un programador para cada dominio. Al principio, uno podría pensar que una instancia del Programador debería manejar los comandos lanzados para cualquier dominio, pero el Programador en sí se ejecuta dentro de una aplicación Laravel, por lo que el "env" bajo el cual se ejecuta se aplica automáticamente a cada comando programado y el --domain La opción --domain no tiene ningún efecto.
Lo mismo se aplica a herramientas externas como Supervisor: si usa Supervisor para comandos artesanales, por ejemplo, el comando queue:work , asegúrese de preparar un comando para cada dominio que desee manejar.
Debido a lo anterior, hay algunos casos en los que el paquete no puede funcionar: en aquellas configuraciones donde no tienes la posibilidad de cambiar, por ejemplo, la configuración del supervisor en lugar de las entradas crontab para el planificador. Aquí se ha señalado un ejemplo de este tipo en el que se ha utilizado una instancia de Docker.
Por último, tenga en cuenta que algunos comandos de Laravel llaman a otros comandos de Artisan desde adentro, obviamente sin la opción --domain . La situación anterior no funciona correctamente porque el subcomando funcionará con el archivo de entorno estándar. Un ejemplo es el comando migrate cuando se utiliza la opción --seed .