【问题标题】:How to use orchestral/tenanti in Laravel 5 to build a multi tenant application with multiple databases?如何在 Laravel 5 中使用 Orchestral/tenanti 构建具有多个数据库的多租户应用程序?
【发布时间】:2020-11-18 13:58:59
【问题描述】:

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

我曾尝试手动管理主数据库迁移和租户迁移,但失败了。因此,我决定借助 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. 迁移文件将在哪里生成和存储?我的意思是我的应用程序中有两种数据库。一组文件用于存储所有租户信息的主数据库,其他文件将用于租户数据库。那么这些将如何存储以及存储在何处?
  2. 我在文档中经常看到“驱动程序”这个词,但我不确定驱动程序到底是什么。
  3. 我将如何处理应用程序的身份验证?我的意思是每当租户登录时,我都必须确保与数据库的连接动态更改。我将如何做到这一点?
  4. 我试图通过包本身的存储库并理解其中的代码但徒劳无功。我对门面、命令总线、服务提供者等设计模式不是很好,这就是为什么我无法理解包的流程或理解它的原因。

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

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

但我收到如下错误:

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

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

【问题讨论】:

  • 好问题!我也有同样的问题。
  • 您是否尝试过联系crynobone?我为他的其他管弦乐项目之一做出了贡献,发现他非常乐于助人且反应迅速。
  • 我个人有,他很有帮助并且确实回复了,但问题是我是一个新手开发人员,只能理解他告诉我的一些事情。大部分都直奔我的头顶。到那时,我也开始朝着另一个方向发展,核心问题是多数据库方法中的多租户。

标签: php mysql laravel laravel-5 multi-tenant


【解决方案1】:

+1 到@morphatic 答案,它在大多数情况下都很准确。

迁移

一组文件用于存储所有租户信息的主数据库,其他文件将用于租户数据库。那么这些将如何存储以及存储在何处?

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

然而,Tenanti 将使用“驱动程序”配置下设置的迁移路径。例如:

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

在这种情况下,迁移将从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' => [ ... ],
];

您可以按照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;
        });
    }
}

这将确保您将 tenant_1 数据库用于 user=1,tenant_2 数据库用于 user=2 等等。

那么 Tenanti 如何检测活跃的用户呢?

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

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

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

【讨论】:

  • 感谢您的帮助。如何通过请求 Illuminate\Http\Request 为单一数据库设置租户?
【解决方案2】:

我从未使用过这个包,但使用您在上面提交的代码,我认为可能接近正确的解决方案。您可能仍需要使用其中的一些值来使它们正确:

迁移路径

由于您使用的是多数据库配置,我相信您应该能够将迁移保持在正常位置,即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' => [ ... ],
    ];

但是,在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' => [ ... ],
    ];

如您所见,每个租户都有自己的数据库实例,该实例可以位于不同的主机上并具有不同的用户名/密码。 Tenanti 需要被告知如何确定使用哪个数据库。这就是the documentation on Database Connection Resolver 所描述的。在他们的示例中,他们使用acme_{$user-&gt;id} 命名了他们的租户数据库,而在我上面的示例中,我使用了user_{$user-&gt;id}

就像我说的,我自己从来没有真正设置过这个,但这些是我根据文档的最佳猜测,并且使用了同一开发人员的其他包。希望这会有所帮助!

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2015-05-10
    • 2015-12-30
    • 1970-01-01
    • 2015-12-16
    • 2021-08-01
    • 2013-06-15
    • 1970-01-01
    • 2014-01-23
    相关资源
    最近更新 更多