sinclairt / multi-tenancy
单一应用程序和单一数据库内的多租户解决方案
Requires (Dev)
- laracasts/testdummy: ~2.0
- laravel/laravel: dev-develop
- phpspec/phpspec: ~2.1
- phpunit/phpunit: ~4.0
README
安装
composer require sinclairt/multi-tenancy
在 config/app.php 数组中注册服务提供者:\Sinclair\MultiTenancy\Providers\MultiTenancySericeProvider::class
可选
发布配置:php artisan vendor:publish
配置
ignore-roles 默认([ 'super-admin' ])
如果您不希望应用程序应用多租户约束,请在此处添加角色,我默认了超级管理员,但您可以自由更改此设置或将其留为空数组,如果您不使用角色
public-space-roles 默认(['super-admin'])
如果您想通过认证路由保护您的公共域,您可以设置哪些角色可以访问此区域
关系名称 默认('tenant')
这是您模型上关系方法的名称,假设用于多对多的是复数形式
关系表 默认('tenants')
租户表的名称
关系类 默认(\App\Models\Tenant::class)
租户模型类的名称
关系多态术语 默认('tenantable')
您模型关系中使用的多态术语
关系外键 默认('tenant_id')
您模型表上使用的外键
关系slug列名 默认('slug')
在租户表中存储slug的列
是否应用回调 默认(null)
如果您想使用自定义逻辑来决定是否应用作用域,请在此处设置回调,用户对象和忽略的角色数组将传递到此
是否应用默认值 默认(true)
如果为true,将自动将作用域应用于模型,这仅在回调为null且忽略的角色为空时使用
角色类 默认(\App\Models\Role::class)
您的角色类的名称 - 如果您不使用角色,请将其留为空字符串
设置
在 config/auth.php
内,您需要设置一个新的守卫和一个新的提供者,如下所示
// guard
'tenant' => [
'driver' => 'session',
'provider' => 'tenants'
]
// provider
'tenants' => [
'driver' => 'tenant',
'model' => App\Models\User::class,
],
您还可以将默认守卫设置为租户或设置一个if语句,否则您在与 Auth
对象交互时需要指定守卫
'guard' => !is_null(constant('TENANT_SLUG')) ? 'tenant' : 'web',
多租户包通过使用全局作用域来限制基于常量 TENANT_SLUG
的查询来工作。此包假设您正在使用子域来控制用户所需的租户;有一个辅助函数可以放在 bootstrap/app.php 中 bootstrapMultiTenancy()
,但当然,您可以随意设置常量,但要使此包正常工作,它必须被设置。
用法
为了避免在每一个数据库表上都有一个外键,多租户包使用模型关系来约束查询。模型可以以三种方式与租户连接
-
直接 - 模型属于租户
-
通过 - 模型通过另一个模型或模型链属于租户
-
多态 - 模型通过多对多关系(包括多态)属于租户
根据上述标准,可以应用三个作用域,并且有特质来简化其中两个的实现:具有相应特质的 BelongsToTenant
和 MorphToTenant
作用域,只需在您的模型中使用它们即可。
另一个作用域 BelongsToTenantThrough
需要稍微多一点设置,但并不多,你只需要在模型启动方法中应用作用域时设置关系,以便通过该关系到达租户,例如
public static function boot()
{
parent::boot();
static::addGlobalScope(new BelongsToTenantThrough('user'));
}
上述示例中的车辆模型以某种方式属于租户,因此我们将通过这种关系到达租户。
你也可以使用点符号来链式调用关系,即 driver.user
,这适用于需要通过一系列关系到达租户的情况。
还有一种情况是,如果你的模型是多态的多对多,并且可能通过各种渠道属于租户,很简单,传递一个所有潜在链接到租户的数组,多租户包只需要找到其中之一
static::addGlobalScope(new BelongsToTenantThrough([ 'users', 'drivers.user', 'locations' ]));
在这个示例中,我们有一个存储与各种模型(用户、驾驶员、位置)相关联的号码的 Phone 模型,因此我们需要检查它是否通过这些连接中的任何一个属于我们的指定租户。
用户模型
由于多租户包允许你使用单个数据库,这意味着如果你想的话,用户可以属于多个租户,这对于管理员角色很有用(尽管我推荐使用 ignore-roles 配置值)。如果你使用子域解决方案进行多租户,这将迫使用户登录到新的租户区域,但这意味着他们可以在租户之间拥有相同的凭据、角色和权限。
如果你的用户模型使用角色,则需要使用 IsMultiTenantUser
特性,它为作用域提供了一段逻辑,同时也使用了 MorphToTenant
特性并为角色关系进行了设置。如果你不使用角色,请确保在你的用户模型中使用 MorphToTenant
特性。