gecche/laravel-multidomain

Laravel 应用在子域上的多租户设置


README

License Laravel Laravel Laravel Laravel Laravel Laravel Laravel

Laravel 多域名

用于在多域名环境中使用 Laravel 的扩展

Laravel Multi Domain

描述

此包允许单个 Laravel 安装与多个 HTTP 域工作。

在许多情况下,不同的客户在代码方面使用相同的应用程序,但在数据库、存储和配置方面并不相同。

此包为每个此类客户提供了一种非常简单的方式来获取特定的环境文件、特定的存储路径和特定的数据库。

文档

版本兼容性

关于兼容性的进一步说明

版本 v1.1.x

  • 从 v1.1.0 到 v1.1.5,版本完全兼容 Laravel 5.5、5.6、5.7、5.8 或 6.0。
  • 从 v1.1.6+ 版本 v1.1.x 仅与 Laravel 5.5 兼容,以便正确运行测试。

截至目前,版本 v1.1.6+、v1.2.x、v1.3.x、v1.4.x、v2.x 和 v3.x 在功能上等效。版本已分开,以便与 Laravel 框架的相应版本运行集成测试。

然而,随着 Laravel 8 的发布,版本 v1.1.14、v1.2.8、v1.3.8 和 v1.4.8 是包括相应 Laravel 5.x 版本新功能的最后一个版本(对该版本仍提供错误修复支持)。2021-02-13 更新:v1.1+ 版本的某些最后功能仍在进行中 :)

v1.0 需要 Laravel 5.1、5.2、5.3 和 5.4(不再维护,且未与 Laravel 5.4 进行测试,但该包的使用与 1.1 相同)

2023-02-20 更新:从 Laravel 10.x 开始,包版本遵循相同的编号。

安装

将 gecche/laravel-multidomain 添加到 composer.json 中的依赖项

{
    "require": {
        "gecche/laravel-multidomain": "11.*"
    }
}

使用 composer update 更新您的包或使用 composer install 进行安装。

您还可以使用 composer require gecche/laravel-multidomain 添加包,并稍后指定您想要的版本。

此包需要覆盖在引导过程的初始阶段对 HTTP 域的检测,以便获取特定的环境文件。因此,此包需要比大多数 Laravel 包更多的配置步骤。

安装步骤

  1. 通过修改 bootstrap/app.php 文件的顶部来替换整个 Laravel 容器。
//use Illuminate\Foundation\Application
use Gecche\Multidomain\Foundation\Application
  1. config/app.php 文件中用扩展的版本覆盖 QueueServiceProvider,如下所示
    '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(),

请注意,如果您由于其他原因修改了 config/app.php 文件,则该文件中可能已经存在上述 providers 条目,唯一重要的行是替换 QueueServiceProvider 的那一行。

  1. 发布配置文件。
php artisan vendor:publish 

(此包使用发现功能。)

遵循以上步骤,您的应用程序将知道正在运行的 HTTP 域,包括 HTTP 和 CLI 请求,以及队列支持。

注意:在 Laravel 11 中,安装比以前简单:如果您使用的是以前的 Laravel 版本,请检查文档中的安装步骤。

Laravel Horizon 安装

此包与 Horizon 兼容,多亏了社区贡献。如果您需要将此包与 Horizon 一起使用,您必须遵循其他两个安装步骤

  1. 按常规安装 Laravel Horizon

  2. 替换 app/Providers/HorizonServiceProvider.php 文件顶部的 Laravel Horizon 引入。

//use Laravel\Horizon\HorizonApplicationServiceProvider;
use Gecche\Multidomain\Horizon\HorizonApplicationServiceProvider;

使用方法

本包向您的应用程序添加了三个命令来管理HTTP域

domain.add artisan命令

主要命令是domain:add命令,它接受要添加到应用程序的HTTP域名作为参数。假设我们有两个域名,site1.comsite2.com,它们共享相同的代码。

我们只需这样做

php artisan domain:add site1.com 

php artisan domain:add site2.com 

这些命令将创建两个新的环境文件,分别是.env.site1.com.env.site2.com,您可以在其中为每个站点放置特定的配置(例如数据库配置、缓存配置和其他配置,通常在环境文件中找到)。

该命令还会在config/domains.php文件中的domains键中添加一个条目。

此外,还创建了两个新文件夹,分别是storage/site1_com/storage/site2_com/。它们的文件夹结构与主存储相同。

storage子结构的自定义必须与config/domain.php文件中的值相匹配。

domain.remove artisan命令

domain:remove命令通过删除其环境文件来从应用程序中删除指定的HTTP域名。例如:

php artisan domain:remove site2.com 

添加force选项将删除域名存储文件夹。

该命令还会从config/domains.php文件中的domains键中删除相应的条目。

domain.update_env artisan命令

domain:update_env命令传递一个JSON编码的数据数组以更新一个或所有环境文件。这些值将被添加到相应的.env文件的末尾。

通过添加domain参数来更新单个域环境文件。

当缺少domain参数时,该命令会更新所有环境文件,包括标准的.env文件。

要更新的域名列表存储在domain.php配置文件中。

例如:

php artisan domain:update_env --domain_values='{"TOM_DRIVER":"TOMMY"}' 

将向所有域名环境文件添加TOM_DRIVER=TOMMY行。

domain.list artisan命令

domain:list命令列出当前安装的域名,包括它们的.env文件和存储路径目录。

列表存储在config/domain.php配置文件中的domains键中。

每次运行domain:adddomain:remove命令时,此列表将自动更新。

config:cache artisan命令

与任何其他artisan命令一样,可以使用此包使用config:cache artisan命令。

请注意,此命令将为在执行此命令的每个域名下生成的文件生成config.php文件。即命令

php artisan config:cache --domain=site2.com 

将生成文件

config-site2_com.php 

更多信息

在运行时,当前HTTP域名存储在Laravel容器中,可以通过此包添加的domain()方法访问。

有一个domainList()方法可用。它返回一个关联数组,包含已安装的域名信息,类似于上面的domain.list命令。

例如:

[ 
   site1.com => [
       'storage_path' => <LARAVEL-STORAGE-PATH>/site1_com,
       'env' => '.env.site1.com'
   ]
] 

在网页中区分HTTP域名

对于应用程序接收到的每个HTTP请求,都会加载特定的环境文件并使用特定的存储文件夹。

如果没有找到特定的环境文件和/或存储文件夹,则使用标准版本。

通过使用PHP变量$_SERVER['SERVER_NAME']来检测正确的HTTP域名。

重要提示:在某些执行环境中,$_SERVER['SERVER_NAME']没有被实例化,因此在此包正常工作之前,您需要自定义HTTP域名的检测,如下所述。

自定义HTTP域名的检测

从版本1.1.15开始,可以通过将Closure作为domain_detection_function_web条目传递给Application构造函数的新domainParams参数来自定义HTTP域名的检测。在以下示例中,HTTP域名的检测依赖于$_SERVER['HTTP_HOST']而不是$_SERVER['SERVER_NAME']

//use Illuminate\Foundation\Application;
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();

在artisan命令中使用多域名

为了区分域名,每个artisan命令都接受一个新选项:domain。例如:

php artisan list --domain=site1.com 

命令将使用相应的域名设置。

关于队列

艺术工匠命令 queue:workqueue:listen 已更新,以接受新的 domain 选项。

php artisan queue:work --domain=site1.com 

通常情况下,上述命令将使用相应的域名设置。

请注意,如果您使用的是 database 驱动,并且有两个共享同一数据库的域名,那么如果您想分别管理每个域的工作,则应使用两个不同的队列。

例如,您可以

  • 在您的 .env 文件中为每个域名设置一个默认队列,例如为 site1.com 设置 QUEUE_DEFAULT=default1,为 site2.com 设置 QUEUE_DEFAULT=default2
  • 通过相应地更改默认队列来更新 queue.php 配置文件
'database' => [
    'driver' => 'database',
    'table' => 'jobs',
    'queue' => env('QUEUE_DEFAULT','default'),
    'retry_after' => 90,
],
  • 启动两个不同的工作者
 php artisan queue:work --domain=site1.com --queue=default1

 php artisan queue:work --domain=site1.com --queue=default2

显然,对于每个其他队列驱动程序都可以这样做,除了 sync 驱动程序。

storage:link 命令

如果您使用 storage:link 命令,并且希望为每个域名创建一个独立的符号链接,您必须手动创建它们,因为到目前为止,该命令始终创建一个名为 storage 的链接,并且该名称是在命令中硬编码的。扩展 storage:link 命令以允许选择名称超出了此包的范围(并且我希望它将直接在未来版本的 Laravel 中完成)。

获取多个存储链接的方法可以是以下。假设有两个域名,即 site1.comsite2.com,它们关联的存储文件夹分别为 storage/site1_comstorage/site2_com

  1. 我们手动为每个域名创建链接
ln -s storage/site1_com/app/public public/storage-site1_com 
ln -s storage/site2_com/app/public public/storage-site2_com 
  1. .env.site1.com.env.site2.com 中添加条目,例如,对于第一个域名
APP_PUBLIC_STORAGE=-site1_com
  1. filesystems.php 配置文件中更改如下
'public' => [
    'driver' => 'local',
    'root' => storage_path('app/public'),
    'url' => env('APP_URL').'/storage'.env('APP_PUBLIC_STORAGE'),
    'visibility' => 'public',
],

此外,如果您在使用此包的单页应用程序(SPA)设置中,您可以通过 .htaccess 或类似解决方案更好地处理每个域的独立公共资源,正如 Scaenicus 在他的 .htaccess 解决方案 中指出的。

将环境文件存储在自定义文件夹中

从版本 1.1.11 开始,在 Application 构造函数中添加了第二个参数,用于选择放置环境文件的文件夹:如果您有成百上千个域名,将环境文件放在 Laravel 的 app 根文件夹中并不是一件愉快的事情。

因此,如果您想使用不同的文件夹,只需将其添加到 bootstrap/app.php 文件的顶部即可。例如,如果您想将环境文件添加到 envs 子文件夹中,只需这样做

//use Illuminate\Foundation\Application;
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();

如果您没有指定第二个参数,则假定标准文件夹。请注意,如果您指定了一个文件夹,也必须在其中放置标准的 .env 文件

默认环境文件和存储文件夹

如果您尝试在某个域名下运行网页或 shell 命令,例如 sub1.site1.com,并且没有为该域名特定的环境文件,即文件 .env.sub1.site1.com 不存在,该包将使用第一个可用的环境文件,通过点分割域名名称。在此示例中,该包将在以下内容中搜索第一个环境文件

.env.site1.com
.env.com
.env

相同的逻辑也适用于存储文件夹。

关于 Laravel 的调度程序、监控程序和一些限制

如果您在设置中使用了 Laravel 的调度器,请记住,还必须使用具有域名选项的命令 schedule:run。因此,您必须为每个域名启动一个调度器。最初可能会想到一个调度器实例应该处理任何域启动的命令,但调度器本身是在 Laravel 应用程序中运行的,所以它运行的“环境”,自动应用于每个计划命令,并且 --domain 选项根本不起作用。

同样适用于外部工具,如 Supervisor:如果您使用 Supervisor 运行 artisan 命令,例如 queue:work 命令,请确保为要处理的每个域名准备一个命令。

因此,在某些情况下,该软件包无法工作:在这些设置中,您没有更改例如调度器 crontab 条目的可能性,而不是监督器配置。例如,在 Docker 实例中使用的情况已经在这里指出。

最后,请注意,一些 Laravel 命令在内部调用其他 Artisan 命令,显然没有 --domain 选项。上述情况无法正常工作,因为子命令将使用标准环境文件工作。一个例子是在使用 --seed 选项时使用 migrate 命令。