misaf / laravel-tenancy-support
Core interfaces and abstractions for Misaf packages
Installs: 1
Dependents: 0
Suggesters: 0
Security: 0
Stars: 1
Watchers: 0
Forks: 0
Open Issues: 0
pkg:composer/misaf/laravel-tenancy-support
Requires
- php: ^8.2
- illuminate/database: ^10.0|^11.0|^12.0
- psr/container: ^1.1.1|^2.0.1
- psr/simple-cache: ^1.0|^2.0|^3.0
Requires (Dev)
- laravel/pint: ^1.27
This package is auto-updated.
Last update: 2026-02-16 02:42:34 UTC
README
Shared tenancy contracts and Eloquent helpers for tenant-aware packages.
Requirements
- PHP 8.2+
- Laravel application runtime (for container + Eloquent usage)
Installation
composer require misaf/laravel-tenancy-support
Contracts
TenantResolver
namespace Misaf\TenancySupport\Contracts; interface TenantResolver { public function getTenantId(): int|string|null; }
TeamResolver
namespace Misaf\TenancySupport\Contracts; interface TeamResolver { public function getTeamId(): int|string|null; }
TenantAccessResolver (optional)
Use this when your resolver can authorize elevated, all-tenant access (for example, super admin).
namespace Misaf\TenancySupport\Contracts; interface TenantAccessResolver { public function canAccessAllTenants(): bool; }
TeamAccessResolver (optional)
Use this when your resolver can authorize elevated, all-team access.
namespace Misaf\TenancySupport\Contracts; interface TeamAccessResolver { public function canAccessAllTeams(): bool; }
Resolver Example
namespace App\Tenancy; use Misaf\TenancySupport\Contracts\TeamAccessResolver; use Misaf\TenancySupport\Contracts\TeamResolver; use Misaf\TenancySupport\Contracts\TenantAccessResolver; use Misaf\TenancySupport\Contracts\TenantResolver; final class RequestTenantResolver implements TenantResolver, TenantAccessResolver, TeamResolver, TeamAccessResolver { public function getTenantId(): int|string|null { return auth()->user()?->tenant_id; } public function getTeamId(): int|string|null { return auth()->id(); } public function canAccessAllTenants(): bool { return (bool) auth()->user()?->is_super_admin; } public function canAccessAllTeams(): bool { return (bool) auth()->user()?->is_super_admin; } }
If TeamResolver is not bound, team scope falls back to auth()->id().
Bind it in your service provider:
use App\Tenancy\RequestTenantResolver; use Misaf\TenancySupport\Contracts\TeamResolver; use Misaf\TenancySupport\Contracts\TenantResolver; public function register(): void { $this->app->scoped(TenantResolver::class, RequestTenantResolver::class); $this->app->scoped(TeamResolver::class, RequestTenantResolver::class); }
Model Usage
use Illuminate\Database\Eloquent\Model; use Misaf\TenancySupport\Concerns\BelongsToTenant; final class Invoice extends Model { use BelongsToTenant; }
Team scope usage:
use Illuminate\Database\Eloquent\Model; use Misaf\TenancySupport\Concerns\BelongsToTeam; final class Task extends Model { use BelongsToTeam; }
Behavior:
- Normal queries are tenant-scoped by
tenant_id. - If tenant is not resolved, reads fail-closed (no rows).
- On create,
tenant_idis auto-filled from current tenant when available. - Team-scoped models apply the same behavior using
team_id.
Super Admin: Explicit All-Tenant Queries
Elevated access is opt-in per operation:
use App\Models\Invoice; use Misaf\TenancySupport\Support\CurrentTenant; $allInvoices = CurrentTenant::withAllTenants( fn () => Invoice::query()->latest()->get() );
withAllTenants(...) throws when current context is not authorized by TenantAccessResolver::canAccessAllTenants().
Team equivalent:
use App\Models\Task; use Misaf\TenancySupport\Support\CurrentTeam; $allTeamTasks = CurrentTeam::withAllTeams( fn () => Task::query()->latest()->get() );
License
MIT. See LICENSE.