按域或子域的多租户

Multitennancy by domain or subdomain

我正在尝试实现一些基本的多租户,其中每个用户都可以使用我域的子域(例如 tenant.mydomain.com)拥有自己的站点,或者将他们自己的自定义域与任何子域一起使用(例如 www.theirdomain.com 或 store.theirdomain.com)。在 sign-up,他们获得了一个子域,但稍后可能会更改为自定义域。系统需要找到正确的租户,无论他们是使用子域还是域访问路由,理想情况下我想对子域和自定义域访问使用相同的路由、控制器功能等。

我自己域的子域在以下情况下正常运行:

Route::group(['domain' => '{subdomain}' . env('SESSION_DOMAIN')], function () {
  // Tenant-specific routes here
});

每个路由接收子域,我可以在相应的控制器方法中相应地查找他们的租户。

我遇到的困难是使用自定义域。我可以做类似下面的事情...

Route::group(['domain' => '{prefix}.{domain}.{suffix}'], function() {
  Route::get('/', function($prefix, $domain, $suffix) {
    return "Domain is: $prefix.$domain.$suffix";
  });
});

...但是为所有路由管理三个变量有点笨拙,我不能使用与子域相同的控制器函数或 blade 文件,因为需要的参数数量不同.我也可以选择不使用数组组并允许任何 domain/subdomain 访问路由并查询域/子域,如下所示...

$current_url = Request::url();
$current_url = str_replace(['https://', 'http://'], '', $current_url);
$subdomain = array_shift(explode('.', $current_url));
// Now look up the tenant based on the domain if is a custom domain or subdomain if it is our domain

但是,我不太确定该代码在哪里,即在控制器方法中,或者更全局的地方(那会在哪里?)。它还会使 blade 文件中的引用路由相当复杂。

有人有什么建议吗?

经过一些初步测试,这是我想出的解决方案...

  • 创建一个新的中间件class - 在句柄函数中,通过$request->url()获取URL并解析它以查看它是否是自定义域或我的域的子域。根据子域或域查询租户 ID,并使用 config(['tennant.id' => $tennant->id]);
  • 将其添加到配置变量中
  • 使用中间件创建路由组Route::middleware(['getTennant'])->group(function () { ... });
  • 这个配置变量可以从任何地方访问,它似乎没有以导致我最初测试中出现意外行为的方式缓存。

我使用的中间件代码是...

这是我的中间件代码:

<?php
namespace App\Http\Middleware;

use Closure;
use Illuminate\Http\Request;
use App\Models\Tennant;

class getTennant
{
    /**
     * Handle an incoming request.
     *
     * @param  \Illuminate\Http\Request  $request
     * @param  \Closure  $next
     * @return mixed
     */
    public function handle(Request $request, Closure $next)
    {
        $current_url = $request->url();
        $current_url = str_replace(['https://', 'http://'], '', $current_url);
        if (str_contains($current_url, env('SESSION_DOMAIN'))) {
            // This is a subdomain
            $current_url = explode('.', $current_url);
            $subdomain = array_shift($current_url);
            // Look up the appropriate tennant by subdomain here
            $tenant = Tenant::where('subdomain', $subdomain)->first();
        }
        else {
            // This is a full custom domain - look up the tennant based on domain
            $tenant = Tennant::where('domain', $current_url)->first();
        }
        if ($tenant) {
            config(['tenant.id' => $tenant->id]);
        }
        else {
            abort(404);
        }
        return $next($request);
    }
}