Laravel 12 中 tenant_id 非内置多租户方案,需手动实现:1. 数据库添加字段并索引;2. 中间件识别租户并注入上下文;3. 全局作用域强制读取隔离;4. 模型事件自动填充 tenant_id 保障写入安全。

在 Laravel 12 中,tenant_id 本身不是框架内置的多租户方案,而是常见的一种「共享数据库、隔离数据表」模式下的字段标识方式。它需要你手动设计 + 中间件 + 查询作用域来实现真正的租户隔离,不能仅靠加一个字段就自动生效。
1. 数据库结构:添加 tenant_id 到需隔离的表
所有属于租户私有数据的表(如 users、posts、orders),都应添加 tenant_id 字段,并建立索引提升查询效率:
php artisan make:migration add_tenant_id_to_posts
迁移中写:
Schema::table('posts', function (Blueprint $table) {
$table->unsignedBigInteger('tenant_id')->nullable();
$table->index('tenant_id');
});
注意:不要设外键指向租户表(如 tenants),除非你确定租户表永不删除;更推荐逻辑关联 + 应用层校验。
2. 租户识别与中间件:把当前租户 ID 注入请求上下文
Laravel 12 没有默认租户解析器,你需要自己决定识别方式(子域名 / 请求头 / URL 参数等)。例如用子域名:
- 在
app/Http/Middleware/IdentifyTenant.php中解析域名,查出tenant_id - 将租户 ID 存入 Laravel 的 request 属性或自定义服务容器实例中(如
app('current.tenant.id')) - 在
AppServiceProvider的boot()中注册全局查询作用域前,确保租户已识别
3. 全局作用域:强制所有查询带上 tenant_id 条件
为每个租户专属模型添加全局作用域:
Laravel Cloud(cloud.laravel.com)。此技能用于处理所有 Laravel Cloud 请求,包括搜索和读取数据。只要任务涉及 Laravel Cloud,即使用此技能。
// app/Models/Post.php
protected static function booted()
{
static::addGlobalScope('tenant', function (Builder $builder) {
$tenantId = app('current.tenant.id') ?? null;
if ($tenantId) {
$builder->where('tenant_id', $tenantId);
}
});
}
⚠️ 注意:该作用域对 withoutGlobalScopes() 和 withTrashed() 等会失效,敏感操作(如后台管理)需显式绕过并做权限校验。
4. 写入控制:确保 tenant_id 不被伪造或遗漏
在模型创建/更新时,自动填充 tenant_id:
- 使用
creating模型事件,在保存前注入当前租户 ID - 重写
fill()或在表单请求验证中过滤掉tenant_id字段,防止用户提交篡改 - 数据库层面可加 CHECK 约束(MySQL 8.0.16+):
CHECK (tenant_id = CURRENT_TENANT_ID()),但 Laravel 不原生支持,需自定义
示例:
protected static function boot()
{
parent::boot();
static::creating(function ($model) {
$model->tenant_id = app('current.tenant.id');
});
}
不复杂但容易忽略的是:全局作用域只影响读,写入和软删除仍需单独防护;同时要确保队列任务、命令行 Artisan 命令也能正确识别租户上下文(常需手动传参或配置环境)。










