xyrintech/tenantable

Laravel 多租户。

1.1.0 2016-02-04 18:47 UTC

This package is not auto-updated.

Last update: 2024-09-24 20:19:47 UTC


README

Packagist License Latest Stable Version Total Downloads Build Status

Laravel Tenantable 包旨在实现无需在每次数据库调用中都访问数据库 ::connection('name'),即可动态启用基于数据库连接的多租户。

安装

只需通过 composer.json 为您的 Laravel 安装添加新包

composer require xyrintech/tenantable

然后运行 composer dump-autoload

更新 composer 后,将 ServiceProvider 添加到 config/app.php 中的 providers 数组中。理想情况下,您应该将其插入到 Illuminate\Database\DatabaseServiceProvider::class 之后,以确保其 boot 方法在数据库可用之后但在任何其他 Service Providers 启动之前被调用。

Laravel 5.1

XyrinTech\Tenantable\TenantableServiceProvider::class,

运行迁移

artisan migrate --path /vendor/xyrintech/tenantable/migrations

然后在您的流程中,使用 Eloquent 方式创建租户

$tenant = new \XyrinTech\Tenantable\Tenant();
$tenant->domain = 'domain.com';
$tenant->driver = 'mysql';
$tenant->host = 'localhost';
$tenant->database = 'domain_com';
$tenant->username = 'root';
$tenant->password = '';
$tenant->prefix = 'prefix';
$tenant->save();

这样就可以了!每次您的应用通过 http://domain.com 访问时,默认数据库连接将使用上述详细信息设置。

兼容性

此 Tenantable 包是为 Laravel 5.1 开发的,我认为它也应该与 5.0 兼容,但它只经过 5.1 及以上版本的测试。

简介

该包通过连接到数据库中保存的连接详情,通过访问的域名简单地解析正确的连接详情。

一旦解析完成,它将使用保存的值设置默认数据库连接。

这避免了根据访问的租户不断切换或程序化访问正确的连接的需要。

这意味着您的所有路由、模型等都将运行在活动租户数据库上(除非显式通过 ::connection('name') 指定)

生命周期

以下是 HTTP 请求期间的工作方式

  • Tenantable 将默认数据库连接的名称复制到 tenantable.database.default 配置区域。
  • Tenantable 通过 Http\Request::getHost() 方法获取主机字符串。
  • Tenantable 在数据库中寻找与该主机匹配的租户。
  • 如果没有找到匹配项,Tenantable 将在 Domains 表中寻找匹配项(如果找到,则使用 Eloquent 关系返回 Tenant 模型)。
  • 找到匹配项后,将匹配项保存为活动租户,并将租户的数据库详情放置在 database.connections.tenant 配置中。
  • 然后默认数据库连接更改为 'tenant',连接被清除(断开/重新连接)。
  • app.url 配置设置为租户的域名。
  • 如果在任一表中找不到匹配项,将触发 TenantNotResolved 事件,并且不会发生任何配置更改。

以下是 artisan 控制台请求期间的工作方式

  • Tenantable 将默认数据库连接的名称复制到 tenantable.database.default 配置区域。
  • Tenantable 注册了一个 --tenant 控制台选项,您可以通过它提供 id、uuid、域名或 *、all 以运行所有租户。
  • Tenantable 检查是否提供了租户选项,如果没有提供,则不解析租户。命令正常运行。
  • 如果找到匹配项,它将在执行命令之前解析租户(设置租户连接详情)。
  • 如果您使用 --tenant 提供了 * 或字符串 all,Tenantable 将对数据库中找到的每个租户运行命令,在每次运行之前设置活动租户。

解析器类

\XyrinTech\Tenantable\Resolver 类负责在 http 和控制台访问期间解析和管理活动租户。

TenantableServiceProvider 将此类注册为单例,以便您可以在应用程序的任何位置通过方法注入或使用 app('XyrinTech\Tenantable\Resolver') 辅助函数使用它。

此类提供了访问或更改活动租户的方法

//fetch the resolver class either via the app() function or by injecting
$resolver = app('XyrinTech\Tenantable\Resolver');

//check if a tenant was resolved
$resolver->isResolved(); // returns bool

//get the active tenant model
$tenant = $resolver->getActiveTenant(); // returns instance of \XyrinTech\Tenantable\Tenant or null

//set the active tenant
$resolver->setActiveTenant(\XyrinTech\Tenantable\Tenant $tenant); // fires a \XyrinTech\Tenantable\Events\SetActiveTenantEvent event

//purge tenant connection
$resolver->purgeTenantConnection();

//reconnect tenant connection
$resolver->reconnectTenantConnection();

租户模型

《\XyrinTech\Tenantable\Tenant》类是一个非常简单的Eloquent模型,具有一些数据库连接属性,以及一个元属性,当访问时会被转换成《Illuminate\Support\Collection》。

每个属性(除了id、uuid、domain、driver、prefix、meta和时间戳)都进行了加密以保证安全,访问时解密,保存时自动加密。

每个模型实例在创建时都会分配一个《uuid》,这个属性不能被设置/更改,因为它是为该租户生成的唯一ID。

使用uuid的原因是允许你在其他地方使用租户的标识符,而无需暴露租户的ID或域名(例如在文件系统中,你可能需要在子文件夹中存储特定于租户的文件)。

该模型可以用其他任何Eloquent模型的方式创建/读取/更新/删除。

//create by mass assignment
\XyrinTech\Tenantable\Tenant::create([
    'domain' => 'http://...'
    ....
]);

//call then save
$tenant = \XyrinTech\Tenantable\Tenant();
$tenant->domain = 'http://...';
...
$tenant->save();

//fetch all tenants
$tenant = \XyrinTech\Tenantable\Tenant::all();

//fetch by domain
$tenant = \XyrinTech\Tenantable\Tenant::where('domain', 'http://..')->first();

事件

《Tenantable》包产生了一些事件,可以在你的应用程序中消费这些事件。

\XyrinTech\Tenantable\Events\SetActiveTenantEvent(\XyrinTech\Tenantable\Tenant $tenant)

当设置租户为活动租户时,会触发此事件,并且有一个公共的《$tenant》属性,包含《\XyrinTech\Tenantable\Tenant》实例。

注意这可能是由于解析器的结果,也可能是在将租户设置为活动状态时触发的。

\XyrinTech\Tenantable\Events\TenantResolvedEvent(\XyrinTech\Tenantable\Tenant $tenant)

当解析器解析租户时,会触发此事件,并且有一个公共的《$tenant》属性,包含《\XyrinTech\Tenantable\Tenant》实例。

注意这仅在请求中触发一次,因为解析器负责此事件。

\XyrinTech\Tenantable\Events\TenantNotResolvedEvent(\XyrinTech\Tenantable\Resolver $resolver)

当解析器无法解析租户时,会触发此事件,并且有一个公共的《$resolver》属性,包含《\XyrinTech\Tenantable\Resolver》实例。

注意这仅在请求中触发一次,因为解析器负责此事件。

关于使用Artisan::call()的说明;

使用《Artisan》外观运行命令时,无法在运行前更改应用程序的活动租户(与控制台Artisan访问不同)。

因此,将使用当前活动租户。

要为每个租户运行命令,你需要使用《Tenant::all()`》获取所有租户,并在设置活动租户后,在foreach循环内运行《Artisan::call()`》方法,如下所示:

//fetch the resolver class either via the app() function or by injecting
$resolver = app('XyrinTech\Tenantable\Resolver');

//store the current tenant
$resolvedTenant = $resolver->getActiveTenant();

//fetch all tenants and loop / call command for each
$tenants = \XyrinTech\Tenantable\Tenant::all();
foreach($tenants as $tenant){
    $resolver->setActiveTenant($tenant);
    $result = \Artisan::call('commandname', ['array' => 'of', 'the' => 'arguments']);
}

//restore the correct tenant
$resolver->setActiveTenant($resolvedTenant);

如果你需要在原始默认连接(即不是租户连接)上运行Artisan外观,请首先调用《Resolver::purgeTenantConnection()`》函数。

//fetch the resolver class either via the app() function or by injecting
$resolver = app('XyrinTech\Tenantable\Resolver');

//store the current tenant
$resolvedTenant = $resolver->getActiveTenant();

//purge the tenant from the default connection
$resolver->purgeTenantConnection();

//call the command
$result = \Artisan::call('commandname', ['array' => 'of', 'the' => 'arguments']);

//restore the tenant connection as the default
$resolver->reconnectTenantConnection();

未来

  • 添加测试
  • 将元项目添加到Laravel配置