Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Nova Multi Tenant Manager Laravel Package

genealabs/nova-multi-tenant-manager

GeneaLabs Nova Multi-Tenant Manager adds multi-tenancy support to Laravel Nova: manage tenants, scope resources and queries per tenant, and let users switch tenants so your Nova admin panel stays tenant-aware.

View on GitHub
Deep Wiki
Context7

Getting Started

Do not composer require this. The repo is archived (last ship 2024‑05‑11), pre‑1.0, ~20 stars, zero dependents. Day‑to‑day leverage comes from treating it as a spec + starting source, not a dependency. Budget a half‑day to a day to evaluate, 1–2 weeks to own a safe fork.

Prerequisites before you spend any time:

  • Laravel 10/11 and PHP 8.2+ (0.7.0 added L11; there is no L12).
  • Laravel Nova, paid and major‑versioned. Note your Nova major now — it is your hard expiry date.
  • An existing tenancy layer (GeneaLabs laravel-multitenancy, single‑DB with scoping, or per‑tenant DBs). This package is an admin UI over tenancy, not tenancy itself. If tenancy isn't built, stop; you'd be building a UI over unbuilt work.

Minimal bootstrap (fork path):

# 1. Mirror, don't vendor-point at upstream
git clone https://github.com/mikebronner/nova-multi-tenant-manager ../internal/nova-tenant-manager
rm -rf ../internal/nova-tenant-manager/.git   # re-init under your namespace

# 2. Consume via path repo while spiking
composer config repositories.nova-tenant-manager path ../internal/nova-tenant-manager
composer require genealabs/nova-multi-tenant-manager:"*@dev"

Record upstream URL, archival date, commit SHA, license, and a named owner in your dependency register before the first commit. That artifact is what auditors and your successor will ask for.

Where to look first, in this order:

  1. The Nova tool class and its NovaServiceProvider registration — that's the surface you actually get.
  2. The Tenant and Domain Nova resources — this is the CRUD you'd otherwise write yourself.
  3. The middleware / service provider boot that calls makeCurrent() (or equivalent) on the tenancy package — this is the security-critical file, not the UI.
  4. config/ — publish it into your app and commit it, so behaviour versions with your app rather than with vendor code.
  5. Whether it ships migrations for domains/tenant pivots, or assumes yours already exist.

First use case to validate: a support engineer, behind SSO and a role check, switches current tenant, opens a tenant‑scoped Nova resource, and sees only that tenant's rows — and a second engineer's switch does not bleed across. If that one flow works and you can prove the negative case, the rest is CRUD.

Implementation Patterns

Register the tool explicitly. Nova tools are opt‑in per panel; don't assume auto‑discovery surfaces it.

// app/Providers/NovaServiceProvider.php
public function tools(): array
{
    return [
        new \App\Nova\Tools\TenantManager, // your fork's class, your namespace
    ];
}

Wrap it behind one adapter. You want exactly one file to touch when Nova ships a major.

// app/Support/Tenancy/TenantSwitcher.php
final class TenantSwitcher
{
    public function switchTo(Tenant $tenant, User $actor): void
    {
        abort_unless($actor->can('switchTenant', $tenant), 403);

        activity('tenancy')->causedBy($actor)
            ->withProperties(['from' => Tenant::current()?->id, 'to' => $tenant->id])
            ->log('tenant switched');

        $tenant->makeCurrent();
    }

    public function forget(): void { Tenant::forgetCurrent(); }
}

Every call site goes through this — controllers, actions, tests. When the fork rots (it will), you replace one class.

Scope tenant‑aware resources by opt‑in trait, then prove it. The classic failure is a new Nova resource that forgets the trait and fails open — it returns all tenants' rows. Institutionalise both halves:

// App\Nova\Concerns\BelongsToCurrentTenant (applied per resource)
// plus a Pest test per resource:
it('does not leak across tenants', function () {
    $a = Tenant::factory()->create(); $b = Tenant::factory()->create();
    $aPost = Post::factory()->for($a)->create();

    actingAsNovaAdmin()->getJson("/nova-api/posts/{$aPost->id}")
        ->assertNotFound(); // after switching to $b
});

Provisioning actions must be queued jobs, never inline. Tenant creation, domain attach, per‑tenant migrations — dispatch with an explicit tenant id in the payload and re‑establish context in the job's handle(). Relying on ambient "current tenant" in a queued action is the #1 way this leaks.

Domain management: normalise on write. Lowercase, strip scheme/www, unique index on domains.domain. Do it in a saving observer on your model, not in the Nova field, so seeders and imports get the same treatment.

User↔tenant assignment lives in policies, not the UI. The pivot UI is trivial; deciding who may grant access is not. One policy (TenantMembershipPolicy) with attachAnyUser / detachAnyUser gates, and canSee/canRun on the Nova action mirroring it.

Keep an operator canary. Ship to internal support/ops first, behind SSO. A bug there is a confused agent; the same bug on a customer-facing switcher is a cross‑tenant incident.

Gotchas and Tips

  • Archived means no CVE path. You are the escalation tier for every bug and every scanner hit. If you cannot name an owner in the same register as production dependencies, that is the answer: don't adopt.
  • Pin hard. ^0.7 at minimum, a commit SHA preferably. Never dev-main, never *. An archived repo can still be force‑pushed or deleted; your mirror kills that failure mode.
  • Middleware ordering is the whole ballgame. It must run after Nova's Authenticate and authorization middleware, or you get an unauthenticated tenant switch. Test it with a guest request, not just as an admin.
  • Session‑based tenant context leaks. makeCurrent() in a long‑running worker (Octane, queue) persists between jobs. Terminate with forgetCurrent() in a terminable middleware and at the top of each queued action's handle(). This package predates any Octane guarantee — assume it's missing.
  • Global scopes fail open. There is no runtime error when a resource skips the trait; you just get everything. Add a lint/CI grep for Nova resources that don't use BelongsToCurrentTenant, and a per‑resource regression test. Review‑checklist item, every PR.
  • Nova internals rot fastest. The tool hooks Nova's tool/resource pipeline. Every Nova major renames something. Put a sunset review on the roadmap now, keyed to your next Nova or Laravel major — whichever lands first. Archived dependencies don't announce expiry.
  • Config caching trips people up. config:cache after publishing; the published config is what your CI matrix should test against, not the package default. Test with --env=production-ish cached config at least once.
  • composer why-not is your only debugging tool here. With zero dependents there are no known‑good combinations. When a conflict appears, it appears at upgrade time, when your options are fewest — resolve illuminate/* constraints early.
  • Migration ordering. Package tables (domains, pivots) must land before your seeders run and after your tenant table exists. If the package ships migrations, publish them and control timestamps yourself.
  • License/provenance before redistribution. Confirm the fork permits internal redistribution and check for trademark/branding constraints in the package name and UI strings before it appears in an admin panel auditors see.
  • No audit trail ships with it. A tenant switcher is a privileged, impersonation‑adjacent surface. Log every switch with actor, from‑tenant, to‑tenant, timestamp, and IP; gate it by role; and add a runbook for on‑call: how to determine which tenant context is current in a given request, and where the audit log lives.
Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
socialdept/atp-orm
cboxdk/laravel-assistant-client
socialdept/atp-resolver
socialdept/tenant-domains
tautvydasr/blade-class-names
socialdept/color-scales
maherelgamil/controller
coleus/widgets
coleus/table
coleus/users
coleus/music
coleus/health
coleus/calendar
coleus/notes
coleus/support
coleus/settings
coleus/apps
seampact/laravel
emmyconceptng/laravel-ng-postcode
jjsquady/round-robin