leek/filament-subtenant-scope 问题修复 & 功能扩展

解决BUG、新增功能、兼容多环境部署,快速响应你的开发需求

邮箱:yvsm@zunyunkeji.com | QQ:316430983 | 微信:yvsm316

leek/filament-subtenant-scope

Composer 安装命令:

composer require leek/filament-subtenant-scope

包简介

Second-level tenancy scope (e.g. service area, region, location) for Filament panels — topnav dropdown filter that scopes Eloquent queries globally.

README 文档

README

Second-level tenancy for Filament panels. Adds a topnav dropdown that scopes every Eloquent query in the panel to a sub-tenant — service area, region, location, branch, department — without touching individual resources.

Filament's built-in tenancy gives you one tenant. This plugin adds another level on top: pick a sub-scope, and resources, widgets, navigation badges, and global search all auto-filter.

screenshot

Why

You already have multi-tenancy (e.g. Company). Inside each company you also need a soft filter — "show me only the North service area" — that:

  • persists across navigation
  • survives logout/login
  • is shareable via URL
  • applies to every model query without per-resource code

This plugin does that with one trait + one ->scopes([...]) array.

Requirements

  • PHP 8.2+
  • Filament v4.x or v5.x
  • Livewire v3 or v4

Installation

composer require leek/filament-subtenant-scope

Styles

Tell your panel theme to compile the plugin's blade utility classes by adding a @source directive to the panel theme configured with ->viteTheme(...):

@import '../../../../vendor/filament/filament/resources/css/theme.css';

@source '../../../../vendor/leek/filament-subtenant-scope/resources/views/**/*.blade.php';

Then rebuild your app assets:

npm run build

Without this, responsive utilities like hidden sm:inline used inside the dropdown won't be compiled into your panel CSS and the dropdown label may collapse on wide screens.

Register the plugin

Register the plugin on your panel and define one or more scopes:

use Filament\Panel;
use Leek\FilamentSubtenantScope\SubtenantScope;
use Leek\FilamentSubtenantScope\SubtenantScopingPlugin;
use App\Models\ServiceArea;

public function panel(Panel $panel): Panel
{
    return $panel
        // ...
        ->plugin(
            SubtenantScopingPlugin::make()
                ->scopes([
                    SubtenantScope::make('service_area', 'Service Area', ServiceArea::class, 'service_area_id')
                        ->icon('heroicon-o-map-pin')
                        ->labelAttribute('name')
                        ->optionsQuery(fn ($user) => ServiceArea::query()
                            ->where('company_id', $user->company_id)
                            ->where('is_active', true)
                            ->orderBy('name')),
                ]),
        );
}

That's the whole topnav setup. The dropdown renders next to the global search.

Opt resources into the scope

Add the HasSubtenantScopes trait and map each scope key to the FK column on the resource's model:

use Filament\Resources\Resource;
use Leek\FilamentSubtenantScope\Concerns\HasSubtenantScopes;

class AppointmentResource extends Resource
{
    use HasSubtenantScopes;

    /** @var array<string, string|null> */
    protected static array $subTenantScopes = [
        'service_area' => 'service_area_id',
    ];
}

The plugin walks every resource in the panel during boot() and registers an Eloquent global scope on the model. Once any resource opts in, all queries on that model auto-filter — list pages, relation managers, navigation badges, widgets, global search.

Custom join logic

If the FK isn't on the model directly, pass null and define a static method named scopeSubTenant{Key}:

class ClientProfileResource extends Resource
{
    use HasSubtenantScopes;

    protected static array $subTenantScopes = ['service_area' => null];

    public static function scopeSubTenantServiceArea(Builder $query, int $id): void
    {
        $query->where(function ($q) use ($id) {
            $q->where('primary_service_area_id', $id)
                ->orWhereHas('serviceAreas', fn ($q) => $q->where('service_areas.id', $id));
        });
    }
}

Behavior

  • URL bookmarks: append ?scope_<key>=<id> to any panel URL — the value is read, persisted, then stripped from the URL on the next render so it's sticky.
  • Single option: when the user has access to exactly one option, the scope renders as a static label (no dropdown) and applies no filter — there's nothing to filter between.
  • Multiple options: dropdown with "All …" plus each option.
  • No options: nothing renders.

Persistence

By default, selections persist for the session. To make them sticky across sessions/devices, register get/set callbacks. The classic pattern is a JSON column on users:

SubtenantScopingPlugin::make()
    ->scopes([/* ... */])
    ->persistUsing(
        get: fn ($user, string $key) => $user->settings['sub_tenant_scopes'][$key] ?? null,
        set: function ($user, string $key, ?int $id): void {
            $settings = $user->settings ?? [];
            $settings['sub_tenant_scopes'][$key] = $id;
            $user->settings = $settings;
            $user->saveQuietly();
        },
    );

Resolution order: URL param → session → user storage. First non-null wins.

Customizing the dropdown

Render hook

Override where the dropdown renders:

use Filament\View\PanelsRenderHook;

SubtenantScopingPlugin::make()
    ->scopes([/* ... */])
    ->renderHook(PanelsRenderHook::TOPBAR_END);

Render the selector yourself

Disable the built-in render hook and embed the Livewire component anywhere:

SubtenantScopingPlugin::make()
    ->scopes([/* ... */])
    ->withoutDropdown();
@livewire(\Leek\FilamentSubtenantScope\Livewire\SubtenantScopeSelector::class)

Override the view

Publish and edit the dropdown blade:

php artisan vendor:publish --tag=filament-subtenant-scope-views

Multiple scopes

Stack as many as you need. Each gets its own dropdown and storage key:

SubtenantScopingPlugin::make()
    ->scopes([
        SubtenantScope::make('region', 'Region', Region::class, 'region_id'),
        SubtenantScope::make('location', 'Location', Location::class, 'location_id'),
    ]);

Resources can opt into one or both:

protected static array $subTenantScopes = [
    'region' => 'region_id',
    'location' => 'location_id',
];

Listening for changes

The Livewire component dispatches sub-scope-changed after every selection (it also triggers a full page reload to refresh server-rendered scoped data):

Livewire.on('sub-scope-changed', ({ scopeKey, value }) => {
    // ...
});

Testing

composer test

How it works

  1. Plugin registers a render hook that pulls the dropdown into the topbar, scopes the manager request-singleton, and walks panel resources during boot() to attach Eloquent global scopes.
  2. Manager resolves the active selection per scope (URL → session → user storage), caches per request.
  3. Trait (HasSubtenantScopes) adds a panel-aware Eloquent global scope to the model. The scope is no-op outside the panel the plugin is registered on.
  4. Livewire selector renders one dropdown per registered scope, writes the selection through the manager, and reloads the page so server-rendered data picks up the new filter.

More Filament plugins by Leek

Premium

  • Filament UI Plus — Enhanced UI components: dual sub-navigation, animated sidebar, horizontal-scroll tables, loading bar, and more.
  • Filament Workflow Engine — Automated workflows with a visual builder, triggers/actions, async execution, and audit logging.
  • Filament Decision Tables — Business rules engine with spreadsheet-style decision tables.

Free & open source

License

MIT. See LICENSE.md.

leek/filament-subtenant-scope 适用场景与选型建议

leek/filament-subtenant-scope 是一款 基于 PHP 开发的 Composer 扩展包,目前已累计 679 次下载、GitHub Stars 达 25, 最近一次更新时间为 2026 年 05 月 02 日, 在 PHP 生态内属于活跃度较高的组件。

它主要适用于以下技术方向: 「leek」 「laravel」 「scope」 「tenancy」 「multi-tenancy」 「filament」 等业务场景。在实际项目中,围绕这些方向常见需要落地的问题包括:接口对接、性能调优、并发安全、与既有框架(Laravel / ThinkPHP / Yii / Webman 等)的兼容适配,以及生产环境的日志埋点与稳定性保障。

我们在过去多个企业项目中使用过 leek/filament-subtenant-scope 或与其功能相近的方案,如果你在选型或落地过程中遇到问题,例如 版本兼容、二次改造、私有化封装、与内部系统对接、生产 BUG 排查,欢迎联系我们协助评估。

围绕 leek/filament-subtenant-scope 我们能提供哪些服务?
定制开发 / 二次开发

基于 leek/filament-subtenant-scope 在你已有业务上做功能扩展、字段裁剪、UI 适配、与内部账号 / 权限 / 日志系统的深度对接。

BUG 修复 & 性能优化

线上偶发问题、内存泄漏、慢查询、并发异常等排查修复;针对高流量场景做缓存、队列、索引层面的调优。

项目外包 & 长期维护

承接完整的项目从需求 → 设计 → 开发 → 上线 → 长期运维;也可按月提供技术保姆服务。

yvsm@zunyunkeji.com QQ:316430983 微信:yvsm316 西安尊云信息科技 · 专注 PHP / Go / 分布式系统研发

统计信息

  • 总下载量: 679
  • 月度下载量: 0
  • 日度下载量: 0
  • 收藏数: 26
  • 点击次数: 20
  • 依赖项目数: 0
  • 推荐数: 0

GitHub 信息

  • Stars: 25
  • Watchers: 1
  • Forks: 3
  • 开发语言: PHP

其他信息

  • 授权协议: MIT
  • 更新时间: 2026-05-02