Laravel 6-使用嵌套租户设置多租户应用程序

问题描述 投票:0回答:1

我目前正在尝试找出为系统设置多租户的最佳方法。我面临的问题是,租户并不一定总是必须是子域,而是可以将其设置为子域的一部分,其中子域可以有多个租户。我似乎在网上找不到任何可以帮助我在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,并设置了以下内容:

我有两个模型子域名

Tenant

Routes / 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添加到需要租户相关数据的控制器方法中,但是还有更好的方法吗?

关于如何改善当前设置的任何建议都会有所帮助。

我目前正在尝试找出为系统设置多租户的最佳方法。我面临的问题是,租户并不一定总是要成为子域,而是可以将其设置为子域的一部分...

php laravel multi-tenant laravel-6 wildcard-subdomain
1个回答
0
投票
© www.soinside.com 2019 - 2024. All rights reserved.