如何使用Laravel 5中的Orchestral/Tenanti来构建一个多数据库租户应用程序?

13

我正在尝试使用 Laravel 5 构建一个应用程序。它应该是一个多租户数据库架构,使用多个数据库。我的雇主出于安全考虑要求这样做。

我试图手动管理主 DB 迁移和租户迁移,但失败了。所以我决定寻求 Laravel 特定包的帮助,这似乎是我需要的。

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

我相信我已经正确地安装了它:

composer require "orchestra/tenanti=~3.0"

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

'providers' => [

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

'aliases' => [

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

],

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

php artisan vendor:publish
好的,请将需要翻译的内容发给我。
return [
    'drivers' => [
        'user' => [
            'model'     => App\User::class,
            'migration' => 'tenant_migrations',
            'path'      => database_path('tenanti/user'),
        ],
    ],
];

目前我还不确定接下来该做什么?

我的疑问如下:

  1. 迁移文件将在哪里生成和存储?我的应用程序中显然有两种类型的数据库。一组文件用于存储所有租户信息的主DB,另一组文件用于租户DB。那这些文件将如何存储和处理?
  2. 在文档中经常看到“driver”这个词,但我不确定它确切的含义是什么。
  3. 如何处理应用程序的身份验证?每当租户登录时,我都需要确保与数据库的连接动态更改。我该如何实现这个功能?
  4. 我尝试审查包的代码库以理解其中的代码,但徒劳无功。像门面、命令总线、服务提供者等设计模式方面我并不擅长,这就是为什么我无法理解包的流程或意义所在的原因。

我尝试运行包自带的一些artisan命令,例如:

php artisan tenanti:install {driver}
php artisan tenanti:make {driver} {name}

但是我遇到了以下错误:

[InvalidArgumentException] 数据库连接 [tenants] 不可用。

我在哪里可以找到资源来了解如何继续进行?


好问题!我也有同样的问题。 - Peter Kota
你尝试过联系crynobone吗?我曾经为他的另一个乐团项目做出过贡献,发现他非常乐于助人和响应迅速。 - morphatic
我个人认为他很有帮助并且回复了我的问题,但问题在于我是一名初学者开发者,只能理解他告诉我的一些内容。大部分内容都超出了我的理解范围。而且那时我也开始朝着另一个方向解决核心问题,即多租户的多数据库方法。 - Rohan
2个回答

11

+1 对 @morphatic 的回答,大部分内容都非常准确。

迁移

一组文件用于存储所有租户信息的主数据库,而另一组文件则用于租户数据库。那么这些文件将如何存储,在哪里存储?

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

Tenanti会使用在“driver”配置下设置的迁移路径。例如:

'path' => database_path('tenanti/user'),

在这种情况下,迁移将从database/tenanti/user创建/迁移(您可以选择其他文件夹,并将使用该文件夹)。设置好后,您可以通过php artisan tenanti:make user create_blogs_table(例如)为用户租户创建新的迁移文件,然后通过php artisan tenanti:migrate user运行迁移(看到Laravel迁移命令和Tenanti之间的相似性了吗?)。

Driver

驱动程序只是租户的分组方式,您可以按用户、公司或团队等进行分组。在某些情况下,您可能需要每个项目使用多种类型的组,否则大多数时候您只需要使用单一的“group”或“driver”。

身份验证或访问数据库

我要如何处理应用程序的身份验证?我的意思是,每当租户登录时,我都必须确保与数据库的连接会动态更改。我该怎么做?

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

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' => [ ... ],
];
你可以按照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;
        });
    }
}

这将确保您对于用户=1使用tenant_1数据库,对于用户=2使用tenant_2数据库,以此类推。

Tenanti是如何检测活动用户的?

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

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

Tenanti::driver('user')->asDefaultDatabase($user, 'tenants_{id}');

谢谢您的帮助。我如何从请求 Illuminate\Http\Request 中设置单个数据库的租户? - Thiago Pereira

6
我从未使用过这个包,但是根据您提交的代码,以下是我认为可能接近正确解决方案的内容。您可能仍需要调整某些值以使其正确:
迁移路径
由于您正在使用多数据库配置,因此我认为您应该能够将迁移保留在正常位置,即database/migrationsTenanti然后会为每个租户在不同的数据库中创建一个完全相同的副本。但是,当您运行php artisan tenanti:install user时,它可能会在database/下创建一个文件夹,指示您应该放置迁移。
“驱动程序”是什么? driver描述了Tenanti是否将使用单个或多个数据库、用于确定不同租户的模型以及存储迁移的位置。这就是您在上面使用的Tenanti配置文件中所确定的内容。
数据库连接选择
您需要按以下方式更新config/database.php。在Laravel应用程序中,您通常会将DB连接设置如下:
<?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' => [ ... ],
    ];

然而,在 Tenanti 多数据库设置的情况下,您需要为每个租户的数据库添加不同的连接信息。要做到这一点,您需要在您的 database.php 配置文件中添加一个新级别(本例假设您正在使用 mysql,但您可以使用任何 DB,甚至为不同的租户使用不同的数据库引擎):
<?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' => [ ... ],
    ];

正如您所看到的,每个租户都有自己的数据库实例,可以位于不同的主机上并具有不同的用户名/密码。需要告诉 Tenanti 如何确定要使用哪个数据库。这就是 Database Connection Resolver 的文档 描述的内容。在他们的示例中,他们使用了 acme_{$user->id} 命名了他们的租户数据库,而在我上面的示例中,我使用了 user_{$user->id}
正如我所说,我从未真正自己设置过这个,但这些都是我根据文档和使用此开发人员的其他软件包的最佳猜测。希望这能帮助到您!

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接