这是一个非常好的问题,它触及了现代 Laravel 应用架构中两个不同层次的概念。简单来说:
- Filament 的多租户 是一个应用层的功能,专注于在同一个应用实例内为不同用户组隔离和展示数据。
- Tenancy 的多租户 是一个架构层的方案,专注于在基础设施层面完全隔离不同客户的数据、代码甚至文件。
它们解决的是完全不同的问题,并且不仅可以共存,而且经常是绝佳的搭配。 为了让你一目了然,我准备了一个对比表格:
特性 | Filament (应用层多租户) | Tenancy (架构层多租户 / SaaS) |
---|---|---|
核心目标 | 数据隔离与视图权限控制 | 为客户提供完全独立、隔离的应用实例 |
隔离级别 | 数据行级 (通过 tenant_id ) | 数据库级、文件级、缓存级 (完全隔离) |
技术实现 | 在业务模型中应用 BelongsToTenant | 创建独立的数据库、存储路径,并动态切换 |
典型场景 | 公司内部分权给不同部门/团队 | 为外部不同公司提供独立的SaaS服务 |
用户感知 | 使用同一个网址,看到不同的数据看板 | 拥有各自的子域名(如 client1.app.com ),感觉像独立应用 |
开发复杂度 | 低至中 | 中至高 |
安全性 | 高 (依赖代码逻辑正确) | 极高 (物理级别隔离) |
成本与维护 | 低 (单一数据库,易于备份维护) | 高 (数据库数量随客户增长,维护更复杂) |
适合客户 | 公司内部的团队、部门 | 外部的企业客户 |
Filament 多租户的适用场景 (Scoped Multi-tenancy)
Filament 的方案非常适合单个组织内部的使用场景,其核心是数据权限管理而非创建独立产品。 典型用例:
- 公司内部的ERP/CRM系统:总公司管理员可以看到所有分公司的数据,而各分公司经理只能查看和操作自己分公司的数据。所有用户都登录到
app.company.com
。 - SaaS 应用中的“团队”或“项目”功能:例如 Notion 或 Linear,你加入一个“工作区”或“团队”后,只能看到该团队内的文档或任务。所有数据都存放在一个数据库中,通过
team_id
区分。 - 学校管理系统:校领导可以看到所有班级数据,而各班老师只能管理自己班级的学生和成绩。
在 Filament 中的实现方式: 就是在你的 Eloquent 模型和 Filament 资源中使用 ScopedToTenant
trait 并定义关系。
// 1. 在模型中定义与租户(如团队)的关系
use Illuminate\Database\Eloquent\Relations\BelongsTo;
class Post extends Model
{
public function team(): BelongsTo
{
return $this->belongsTo(Team::class);
}
}
// 2. 在Filament资源中应用Scope
use Filament\Resources\Resource;
class PostResource extends Resource
{
// 这个Trait会自动过滤当前用户所属团队的数据
use HasScopedTenant;
public static function getTenantRelationship(): BelongsTo
{
return static::getModel()::team();
}
public static function getTenantOwnershipRelationship(\Illuminate\Database\Eloquent\Model $tenant): BelongsTo
{
return static::getModel()::team();
}
}
Tenancy 多租户的适用场景 (SaaS Multi-tenancy)
Tenancy 的方案旨在构建一个真正的多租户SaaS平台,每个客户都感觉自己在使用一个独立的应用。 典型用例:
- 标准的B2B SaaS产品:例如 Shopify、Intercom。你注册后,会获得
yourstore.mysaas.com
这样的子域名和一个完全独立的环境,其他客户的存在对你而言是不可见的。 - 需要为客户提供高度定制化服务的平台:例如,为不同客户启用不同的功能模块,或者允许他们安装自己的自定义插件。
- 对数据隔离和安全性有极端要求的行业:例如金融、医疗领域的应用,法规要求客户数据必须物理隔离。
实现方式(以 stancl/tenancy
为例): 这涉及到路由、数据库连接和文件存储的动态配置。
# routes/tenant.php
Route::middleware([
'web',
InitializeTenancyByDomain::class, // 关键中间件:根据域名初始化租户
PreventAccessFromCentralDomains::class,
])->group(function () {
Route::get('/', function () {
// 这个 '/' 对于不同租户是不同的环境
return view('welcome');
});
// 你的租户路由
});
如何结合使用?(Recommended Architecture)
这两种方案非但不冲突,结合起来反而能构建出强大且灵活的系统。 经典架构: 使用 Tenancy 为每个客户创建一个独立的 SaaS 实例,然后在每个实例内部使用 Filament 的多租户功能来管理该客户内部的团队和权限。 举例说明: 假设你开发了一个名为 “CloudCRM” 的SaaS产品。
- 第一层隔离 (Tenancy):
- 你的客户 “公司A” 注册后,获得子域名
companya.cloudcrm.com
和一个独立的数据库。 - 你的客户 “公司B” 注册后,获得子域名
companyb.cloudcrm.com
和另一个独立的数据库。 - 公司A 和 公司B 的数据完全隔离,互不可见。
- 你的客户 “公司A” 注册后,获得子域名
- 第二层隔离 (Filament):
- 公司A 的老板登录到
companya.cloudcrm.com
后,可以创建多个“团队”,例如“销售部”和“市场部”。 - 他使用 Filament 的管理后台 分配权限:销售团队经理只能看到销售相关的客户和订单,市场团队亦然。
- 这一切都发生在 公司A 自己的独立数据库内。
- 公司A 的老板登录到
总结与选择建议
- 如果你的用户属于同一个组织(例如公司、学校的内部用户),你只是想在他们之间分配数据查看和操作的权限,那么只使用 Filament 的多租户 就足够了。它简单、高效、易于维护。
- 如果你是在构建一个面向外部不同企业客户的SaaS产品,那么你需要使用 Tenancy 来为他们提供真正隔离的实例。在此基础上,你可以在每个客户实例内部使用 Filament 的多租户 来管理该客户内部的用户权限。
- 绝大多数情况下,Filament 的多租户是其作为Admin面板的“内部功能”,而Tenancy是构建整个应用的“底层架构”。它们处于不同层级,协同工作。