0

我正在尝试使用 Laravel 5 构建一个多租户应用程序。我知道已经存在诸如 Hyn 和 AuraEQ 之类的软件包,但我仍然觉得这些很难理解,我想制作自己的(简化)版本。这样我就可以确切地知道引擎盖下发生了什么。在开始之前,我已经阅读了互联网上的几乎所有内容,但是关于这个主题的信息并不多。

我想到的方法非常简单:

  • 定义 中的租户app/config/tenant.php和对应的数据库连接app/config/database.php。我认为将租户存储在“主”数据库中是多余的。
  • 注册服务提供商以启用多租户
  • 在检测(活动)租户的 IoC 容器中注册一个 Singleton
  • 扩展所有 Eloquent 模型并覆盖getConnectionName()活动租户数据库中的查询

每个网站(租户)在存储路径中都有自己的文件夹。假设我有一个名为“coding.com”的网站,路径将类似于:app/storage/tenants/coding-com. 租户当前拥有自己的视图和路线。

到目前为止一切顺利,这是代码:

class TenantServiceProvider extends ServiceProvider
{
    /**
     * Bootstrap the application services.
     *
     * @return void
     */
    public function boot()
    {
        $this->app->singleton('Tenant', function () {
            $tenants = config('tenant.hosts');
            $host = Request::server('HTTP_HOST');

            if(array_key_exists($host, $tenants)) {
                return $tenants[$host];
            }

            return null;
        });

        $tenant = $this->app->make('Tenant');
        $directory = tenant_path($tenant);

        if($tenant && is_dir($directory)) {
            // Load views from the current tenant directory
            $this->app['view']->addLocation($directory . '/views');

            // Load optional routes from the current tenant directory
            if(file_exists($directory . '/routes.php')) require_once $directory . '/routes.php';
        }

        // Load base views, these will be overridden by tenant views with the same name
        $this->app['view']->addLocation(realpath(base_path('resources/views')));
    }
}

定义租户:

return [

    /**
     * Base path of the tenant's directory.
     */
    'path' => storage_path('tenants'),

    /**
     * This is where the tenants are defined.
     */
    'hosts' => [
        'coding.com' => [
            'id'    => 'coding-com', // Connection within config/database.php is also named coding-com
            'https' => false,
        ],
        'other.com' => [
            'id'    => 'other-nl',
            'https' => false,
        ]
    ]

];

而且我必须使用一个名为“TentantModel”的类来扩展每个 Eloquent 模型,它会覆盖该getConnectionName()方法:

/**
 * Get the current connection name for the model.
 *
 * @return string
 */
public function getConnectionName()
{
    $tenant = app()->make('Tenant');

    if($tenant) {
        return array_get($tenant, 'id');
    }

    return parent::getConnectionName();
}

我为大量代码道歉,但这使这个想法更容易理解。

这给我带来了几个问题:

  • 我将如何为每个租户单独管理资产?
  • 使用 Singleton 是否对这种方法有意义?这是我唯一能想到的拥有某种全局变量并且它工作正常,但我怀疑 Singleton 是否适用于这种东西。
  • 这种方法是否有任何缺点,我在考虑时没有考虑到这些问题?

只回答一个将不胜感激。提前致谢!

4

0 回答 0