按域或子域的多租户
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);
}
}
我正在尝试实现一些基本的多租户,其中每个用户都可以使用我域的子域(例如 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);
}
}