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 Application
QueueServiceProvider
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:cache
El 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
.