我目前正在尝试找出为系统设置多租户的最佳方法。我面临的问题是,租户并不一定总是必须是子域,而是可以将其设置为子域的一部分,其中子域可以有多个租户。我似乎在网上找不到任何可以帮助我在Laravel 6中进行设置的内容。
系统要求:
系统必须使用单个数据库进行设置,该数据库将使用tenant_id来确定哪些数据属于租户。
我目前将所有子域数据存储在具有以下结构的表“子域”中:
id subdomain (unique) status nested_tenants (yes/no)
nested_tenants
列确定子域本身是租户(= 0)还是具有多个租户(= 1)。如果子域没有嵌套的租户,则我们设置tenant_id=subdomain
如果子域确实有嵌套的租户,那么我们将所有这些都存储在具有结构的表中:
id subdomain (the sub-domain it belongs to) tenant (the tenant - unique field) name status
并且我们从此表中设置
tenant_id=tenant
。
如果我们已经为子域嵌套了租户,那么在用户登录之前,我们无法确定当前的租户是什么。我们必须从用户详细信息中获取tenant_id。
我当前的设置:
我一直关注此article,并设置了以下内容:
我有两个模型子域名
,TenantRoutes / web.php:
:Route::group([ 'middleware' => \App\Http\Middleware\IdentifySubdomain::class, 'as' => 'tenant:', 'namespace' => 'Tenant' ], function () { // custom auth routes Route::get('/login', 'Auth\LoginController@index')->name('login'); Route::post('/login', 'Auth\LoginController@login'); Route::get('/home', 'HomeController@index')->name('home'); });
中间件标识子域
:class IdentifySubdomain { protected $tenantManager; public function __construct(TenantManager $tenantManager) { $this->tenantManager = $tenantManager; } public function handle($request, Closure $next) { /** need to check whether subdomain is valid * if subdomain is valid return the request page else error message. * if subdomain is true it will check the nested_tenants value from db. * if nested_tenants is false it will set the tenant to current subdomain * else the tenant is not set yet. */ // get host domain and subdomain domain $host = $request->getHost(); // get subdomain position $pos = strpos($host, env('TENANT_DOMAIN')); $subdomain = substr($host, 0, $pos - 1); if ($pos !== false && $this->tenantManager->checkSubdomain($subdomain)) { return $next($request); } throw new NotFoundHttpException; } }
TenantManager
class TenantManager { private $tenant; public function setTenant(?Tenant $tenant) { $this->tenant = $tenant; return $this; } public function getTenant(): ?Tenant { return $this->tenant; } public function loadTenant(string $identifier): bool { $tenant = Tenant::query()->where('tenant', '=', $identifier)->first(); if ($tenant) { $this->setTenant($tenant); return true; } return false; } public function checkSubdomain(string $identifier) : bool { $subdomain = Subdomain::query()->where('subdomain', '=', $identifier)->first(); if ($subdomain) { if ($subdomain->nested_tenants) { // tenant not found yet so do not set tenant return true; } else { return $this->loadTenant($identifier); } } return false; } }
服务提供商
:class TenantServiceProvider extends ServiceProvider { public function register() { $manager = new TenantManager; $this->app->instance(TenantManager::class, $manager); $this->app->bind(Tenant::class, function() use ($manager) { $tenant = $manager->getTenant(); if ($tenant === null) { return new Tenant; } return $manager->getTenant(); }); } }
登录控制器
class LoginController extends Controller { public function __construct() { $this->middleware('guest')->except('logout'); } ... public function login(Request $request, Tenant $tenant) { $request->validate([ 'email' => ['required', 'email', 'max:255'], 'password' => ['required'], ]); $credentials = $request->only('email', 'password'); $credentials['status'] = 1; if ($tenant->id) { $credentials['tenant_id'] = $tenant->tenant; } if (Auth::attempt($credentials)) { return redirect()->intended('home'); } return Redirect::to('login')->withSuccess('Login Failed! You entered invalid credentials'); } ... }
问题
我主要担心的是,我不认为这是跟踪租户的最佳方法。我需要它,以便在设置租户后就可以在整个应用程序中使用它,而不必始终先检查用户是否首先经过身份验证-然后才能获取租户。我目前正在将Tenant $tenant
添加到需要租户相关数据的控制器方法中,但是还有更好的方法吗?
关于如何改善当前设置的任何建议都会有所帮助。
我目前正在尝试找出为系统设置多租户的最佳方法。我面临的问题是,租户并不一定总是要成为子域,而是可以将其设置为子域的一部分...