如何在Laravel 5中使用orchestral/tenanti构建具有多个数据库的多租户应用程序?

Roh*_*han 13 php mysql multi-tenant laravel laravel-5

我正在尝试使用Laravel 5进行构建和应用.它应该是一个使用多个数据库的多租户数据库架构.我的雇主出于安全目的要求这样做.

我尝试过手动管理主数据库迁移和租户迁移但是失败了.所以我决定接受Laravel特定包的帮助,这应该是我所需要的.

Tenanti提供了一种方法来解决我的目的,但问题是我是一名新手开发人员,我无法完全理解如何在我的应用程序中使用它.

我已正确安装它我相信:

composer require "orchestra/tenanti=~3.0"
Run Code Online (Sandbox Code Playgroud)

在配置应用程序文件中添加这些提供程序和别名:

'providers' => [

    // ...
    Orchestra\Tenanti\TenantiServiceProvider::class,
    Orchestra\Tenanti\CommandServiceProvider::class,
],

'aliases' => [

    'Tenanti' => Orchestra\Support\Facades\Tenanti::class,

],
Run Code Online (Sandbox Code Playgroud)

最后发布配置并根据多个数据库的文档进行调整:

php artisan vendor:publish
Run Code Online (Sandbox Code Playgroud)

return [
    'drivers' => [
        'user' => [
            'model'     => App\User::class,
            'migration' => 'tenant_migrations',
            'path'      => database_path('tenanti/user'),
        ],
    ],
];
Run Code Online (Sandbox Code Playgroud)

在这一点上,我仍然模糊下一步做什么?

我的怀疑如下:

  1. 迁移文件将在何处生成和存储?我的意思是我的应用程序中有两种数据库.一组文件用于主DB,它将存储所有租户信息,其他文件用于租户DB.那么这些存储的方式和位置如何?
  2. 我在文档中看到了很多"驱动程序"这个词,但我不确定驱动程序是什么.
  3. 我将如何处理应用程序的身份验证?我的意思是每当租户登录时,我都必须确保与数据库的连接动态变化.我将如何做到这一点?
  4. 我试图通过包本身的存储库,并理解内部的代码,但徒劳无功.当涉及到外观,命令总线,服务提供商等设计模式时,我不是很好,这就是为什么我无法理解包的流程或理解它的原因.

我尝试运行一些附带的工匠命令,如:

php artisan tenanti:install {driver}
php artisan tenanti:make {driver} {name}
Run Code Online (Sandbox Code Playgroud)

但我收到的错误是这样的:

[InvalidArgumentException]数据库连接[租户]不可用.

我在哪里可以找到资源来了解如何进行此操作?

cry*_*one 11

+1给@morphatic答案,它对大多数东西都很安静准确.

移民

一组文件用于主DB,它将存储所有租户信息,其他文件用于租户DB.那么这些存储的方式和位置如何?

对于您的主数据库,您应该能够使用默认值database/migration并使用php artisan make:migrationphp artisan migrate.

但Tenanti将使用"驱动程序"配置下设置的迁移路径.例如:

'path' => database_path('tenanti/user'),
Run Code Online (Sandbox Code Playgroud)

在这种情况下,将创建/迁移迁移database/tenanti/user(您可以选择其他文件夹,它将使用该文件夹).设置完成后,您可以通过php artisan tenanti:make user create_blogs_table(作为示例)为用户租户创建新的迁移文件并运行迁移php artisan tenanti:migrate user(请参阅Laravel迁移命令和Tenanti之间的相似性?).

司机

驱动程序只是租户的分组,您可以按用户,公司或团队等对其进行分组.每个项目可能需要多个类型的组,否则大多数情况下您只使用单个"组"或"司机".

身份验证或访问数据库

我将如何处理应用程序的身份验证?我的意思是每当租户登录时,我都必须确保与数据库的连接动态变化.我将如何做到这一点?

首先,您需要考虑如何区分每个租户.大多数时候我会看到人们倾向于选择子域名.因此,在这种情况下,您需要使用中间件检查子域是否属于任何用户(通过查询主数据库),然后连接到属于该用户的数据库.

Tenanti不管理流程的这一部分,因为每个人在这方面都有不同的风格,但我们提供了一个代码,可以从基础数据库配置动态连接到数据库租户.

假设你有以下配置:

<?php

return [
    'fetch' => PDO::FETCH_CLASS,
    'default' => 'primary',
    'connections' => [
        'primary' => [
            //
        ],
        'tenants' => [
                'driver'    => 'mysql',
                'host'      => 'dbhost',     // for user with id=1
                'username'  => 'dbusername', // for user with id=1
                'password'  => 'dbpassword', // for user with id=1
                'charset'   => 'utf8',
                'collation' => 'utf8_unicode_ci',
                'prefix'    => '',
                'strict'    => false,
            ],
       ],
    ],
    'migrations' => 'migrations',
    'redis' => [ ... ],
];
Run Code Online (Sandbox Code Playgroud)

您可以按照https://github.com/orchestral/tenanti#multi-database-connection-setup中提供的步骤操作,并添加以下代码.

<?php namespace App\Providers;

use Orchestra\Support\Facades\Tenanti;

class AppServiceProvider extends ServiceProvider
{
    public function boot()
    {
        Tenanti::setupMultiDatabase('tenants', function (User $entity, array $template) {
            $template['database'] = "tenant_{$entity->getKey()}";

            return $template;
        });
    }
}
Run Code Online (Sandbox Code Playgroud)

这将确保您使用tenant_1user = 1的tenant_2数据库,user = 2的数据库等等.

那么Tenanti如何检测哪个用户有效?

这是您需要在中间件中添加逻辑的地方.

$user = App\User::whereSubdomain($request->route()->parameter('tenant'))->first();

Tenanti::driver('user')->asDefaultDatabase($user, 'tenants_{id}');
Run Code Online (Sandbox Code Playgroud)


mor*_*tic 6

我从来没有使用过这个软件包,但是使用上面提到的代码,我认为可能接近正确的解决方案.您可能仍需要使用其中一些值来使它们正确:

迁移路径

由于您使用的是多数据库配置,我相信您应该能够将迁移保持在正常位置,即database/migrations.Tenanti然后,将为不同数据库中的每个租户创建数据库的精确副本.但是,当您运行php artisan tenanti:install user它时,实际上可能会创建一个文件夹database/,指示您应该将迁移放在何处.

什么是"司机"?

driver说明是否Tenanti将使用一个或多个数据库,什么型号,以用于确定不同的租户,以及在哪里存储迁移.这是您在Tenanti上面使用的配置文件中标识的内容.

数据库连接选择

您需要更新config/database.php如下.在普通 Laravel应用程序中,您将按如下方式设置数据库连接:

<?php
    return [
        'fetch' => PDO::FETCH_CLASS,
        'default' => env('DB_CONNECTION', 'mysql'),
        'connections' => [
            'sqlite' => [ ...DB connection info... ],
            'mysql'  => [ ...DB connection info... ],
            'pgsql'  => [ ...DB connection info... ],
            'sqlsrv' => [ ...DB connection info... ],
        ],
        'migrations' => 'migrations',
        'redis' => [ ... ],
    ];
Run Code Online (Sandbox Code Playgroud)

但是,在Tenanti多数据库设置的情况下,您需要为每个租户的数据库添加不同的连接信息.为此,您需要在database.php配置文件中添加一个新级别(此示例假定您使用的是mysql,但您可以使用任何数据库,甚至不同的租户使用不同的数据库引擎):

<?php
    return [
        'fetch' => PDO::FETCH_CLASS,
        'default' => env('DB_CONNECTION', 'mysql'),
        'connections' => [
            'tenants' => [
                'user_1' => [
                    'driver'    => 'mysql',
                    'host'      => 'dbhost',     // for user with id=1
                    'database'  => 'dbname',     // for user with id=1
                    'username'  => 'dbusername', // for user with id=1
                    'password'  => 'dbpassword', // for user with id=1
                    'charset'   => 'utf8',
                    'collation' => 'utf8_unicode_ci',
                    'prefix'    => '',
                    'strict'    => false,
                ],
                'user_2' => [
                    'driver'    => 'mysql',
                    'host'      => 'dbhost',     // for user with id=2
                    'database'  => 'dbname',     // for user with id=2
                    'username'  => 'dbusername', // for user with id=2
                    'password'  => 'dbpassword', // for user with id=2
                    'charset'   => 'utf8',
                    'collation' => 'utf8_unicode_ci',
                    'prefix'    => '',
                    'strict'    => false,
                ],
           ],
        ],
        'migrations' => 'migrations',
        'redis' => [ ... ],
    ];
Run Code Online (Sandbox Code Playgroud)

如您所见,每个租户都有自己的数据库实例,可以位于不同的主机上,并具有不同的用户名/密码.Tenanti需要告诉我们如何确定使用哪个数据库.这就是数据库连接解析器上的文档所描述的内容.在他们的例子中,他们使用acme_{$user->id}了我的租户数据库,而在我上面的例子中,我使用了user_{$user->id}.

就像我说的那样,我从来没有真正设置过这个,但这些是基于文档的最好的猜测,并且使用了同一个开发人员的其他软件包.希望这可以帮助!