PHPackages                             ubxty/multi-tenant-laravel-permissions - PHPackages - PHPackages  [Skip to content](#main-content)[PHPackages](/)[Directory](/)[Categories](/categories)[Trending](/trending)[Leaderboard](/leaderboard)[Changelog](/changelog)[Analyze](/analyze)[Collections](/collections)[Log in](/login)[Sign up](/register)

1. [Directory](/)
2. /
3. [Authentication &amp; Authorization](/categories/authentication)
4. /
5. ubxty/multi-tenant-laravel-permissions

ActiveLibrary[Authentication &amp; Authorization](/categories/authentication)

ubxty/multi-tenant-laravel-permissions
======================================

A powerful, multi-tenant capable permission handling package for Laravel with role-based access control, wildcard permissions, high-performance caching, and complete domain/subdomain-based tenancy support.

2.0.0(3mo ago)010MITPHPPHP ^8.2CI passing

Since Jan 23Pushed 3mo agoCompare

[ Source](https://github.com/ubxty/multi-tenant-laravel-permissions)[ Packagist](https://packagist.org/packages/ubxty/multi-tenant-laravel-permissions)[ Docs](https://github.com/ubxty/multi-tenant-laravel-permissions)[ RSS](/packages/ubxty-multi-tenant-laravel-permissions/feed)WikiDiscussions main Synced 1mo ago

READMEChangelog (2)Dependencies (7)Versions (3)Used By (0)

Multi-Tenant Laravel Permissions
================================

[](#multi-tenant-laravel-permissions)

[![Latest Version on Packagist](https://camo.githubusercontent.com/f486b146af5ac72c13f03fcc5b5b7b5e338ec0a8817f0df31f023ec41a58da27/68747470733a2f2f696d672e736869656c64732e696f2f7061636b61676973742f762f75627874792f6d756c74692d74656e616e742d6c61726176656c2d7065726d697373696f6e732e7376673f7374796c653d666c61742d737175617265)](https://packagist.org/packages/ubxty/multi-tenant-laravel-permissions)[![Total Downloads](https://camo.githubusercontent.com/1950a95f8239db84e7e05eb3158fd5a8f763c64aa4a1d37775705553d76340d0/68747470733a2f2f696d672e736869656c64732e696f2f7061636b61676973742f64742f75627874792f6d756c74692d74656e616e742d6c61726176656c2d7065726d697373696f6e732e7376673f7374796c653d666c61742d737175617265)](https://packagist.org/packages/ubxty/multi-tenant-laravel-permissions)[![License](https://camo.githubusercontent.com/30f354a6f64b1fd9258993bb4626c3a6e7fd00ab3cbc2d89fa944bc214d118a1/68747470733a2f2f696d672e736869656c64732e696f2f7061636b61676973742f6c2f75627874792f6d756c74692d74656e616e742d6c61726176656c2d7065726d697373696f6e732e7376673f7374796c653d666c61742d737175617265)](https://packagist.org/packages/ubxty/multi-tenant-laravel-permissions)[![PHP Version](https://camo.githubusercontent.com/5738b553401beb09496b2c05b3a6b26609e0ef4a81875a946ac6ae2f833b1dc3/68747470733a2f2f696d672e736869656c64732e696f2f7061636b61676973742f7068702d762f75627874792f6d756c74692d74656e616e742d6c61726176656c2d7065726d697373696f6e732e7376673f7374796c653d666c61742d737175617265)](https://packagist.org/packages/ubxty/multi-tenant-laravel-permissions)

A powerful, multi-tenant capable permission handling package for Laravel with role-based access control, wildcard permissions, high-performance caching, and **complete domain/subdomain-based tenancy support**.

**Built by [Ubxty](https://ubxty.com)**

---

Table of Contents
-----------------

[](#table-of-contents)

- [Features](#features)
- [Requirements](#requirements)
- [Installation](#installation)
- [Quick Start Guide](#quick-start-guide)
    - [Tenancy Setup](#tenancy-setup)
    - [Permissions Setup](#permissions-setup)
- [Tenancy Deep Dive](#tenancy-deep-dive)
    - [Configuration Options](#tenancy-configuration-options)
    - [Middleware Reference](#middleware-reference)
    - [Model Scoping](#model-scoping)
    - [Helper Functions &amp; Facade](#helper-functions--facade)
    - [Context Switching](#context-switching)
- [Permissions &amp; Roles](#permissions--roles)
    - [Creating &amp; Managing](#creating--managing-roles-and-permissions)
    - [Checking Permissions](#checking-permissions)
    - [Tenant-Scoped Permissions](#tenant-scoped-permissions)
    - [Wildcard Permissions](#wildcard-permissions)
    - [Permission Settings](#permission-settings--scopes)
- [Caching](#high-performance-caching)
- [Middleware](#middleware)
- [Blade Directives](#blade-directives)
- [Commands](#commands)
- [Upgrading / Migration](#upgrading-from-existing-tenancy)
- [Testing](#testing)

---

Features
--------

[](#features)

### Permissions &amp; Roles

[](#permissions--roles)

- 🏢 **Multi-Tenancy Support** - Tenant-scoped roles and permissions out of the box
- 🔑 **UUID Support** - First-class UUID support for roles and permissions
- ⚡ **High-Performance Caching** - In-memory + database JSON caching for minimal DB queries
- 🃏 **Wildcard Permissions** - Grant access to permission groups with `posts.*`
- 🎛️ **Permission Scopes** - Attach JSON settings/constraints to permissions per role
- 🛡️ **Middleware** - Ready-to-use permission and role middleware
- 🎨 **Blade Directives** - Convenient `@role`, `@hasrole`, `@hasanyrole`, `@hasallroles`, `@unlessrole`

### Tenancy

[](#tenancy)

- 🌐 **Domain/Subdomain Resolution** - Automatic tenant resolution from `tenant.yourapp.com`
- 🔒 **Model Scoping** - Automatic query filtering with `TenantScoped` trait
- 👤 **User-Based Resolution** - Resolve tenant from authenticated user's `tenant_id`
- 🔄 **Context Switching** - Run code within specific tenant contexts
- 🛠️ **Helper Functions** - `tenant()`, `tenant_id()`, `has_tenant()`, `run_for_tenant()`
- 🎭 **Facade Support** - `Tenant::get()`, `Tenant::run()`, etc.
- 📦 **Laravel 10, 11 &amp; 12** - Full support for modern Laravel versions

---

Requirements
------------

[](#requirements)

- PHP 8.2+
- Laravel 10.x, 11.x, or 12.x

---

Installation
------------

[](#installation)

Install the package via Composer:

```
composer require ubxty/multi-tenant-laravel-permissions
```

Publish the configuration and migrations:

```
php artisan vendor:publish --provider="Ubxty\MultiTenantLaravelPermissions\MultiTenantPermissionsServiceProvider"
```

Run the migrations:

```
php artisan migrate
```

---

Quick Start Guide
-----------------

[](#quick-start-guide)

### Tenancy Setup

[](#tenancy-setup)

Get multi-tenancy working in your app in 5 steps:

#### Step 1: Configure your tenant model

[](#step-1-configure-your-tenant-model)

```
// config/multi-tenant-permissions.php
'tenancy' => [
    'tenant_model' => App\Models\Company::class,
    'tenant_foreign_key' => 'company_id',
    'subdomain_column' => 'subdomain',
],
```

#### Step 2: Create your Tenant model

[](#step-2-create-your-tenant-model)

```
// app/Models/Company.php
namespace App\Models;

use Illuminate\Database\Eloquent\Model;
use Ubxty\MultiTenantLaravelPermissions\Tenancy\Contracts\Tenant;
use Ubxty\MultiTenantLaravelPermissions\Tenancy\Traits\IsTenant;

class Company extends Model implements Tenant
{
    use IsTenant;

    protected $fillable = ['name', 'subdomain', 'email'];
}
```

#### Step 3: Add middleware (Laravel 11+)

[](#step-3-add-middleware-laravel-11)

```
// bootstrap/app.php
->withMiddleware(function (Middleware $middleware) {
    $middleware->web(prepend: [
        \Ubxty\MultiTenantLaravelPermissions\Tenancy\Middleware\SetTenantFromHost::class,
    ]);
})
```

#### Step 4: Scope your models

[](#step-4-scope-your-models)

```
// app/Models/Project.php
use Ubxty\MultiTenantLaravelPermissions\Tenancy\Traits\TenantScoped;

class Project extends Model
{
    use TenantScoped;
}
```

#### Step 5: Use helpers anywhere

[](#step-5-use-helpers-anywhere)

```
$tenant = tenant();           // Get current tenant model
$id = tenant_id();            // Get current tenant ID
if (has_tenant()) { ... }     // Check if tenant is set

// Run code for a specific tenant
run_for_tenant($tenant, function ($tenant) {
    return Project::all(); // Scoped to $tenant
});
```

📚 **[Full Tenancy Documentation →](docs/tenancy/SETUP.md)**

---

### Permissions Setup

[](#permissions-setup)

#### Step 1: Add HasRoles trait to User model

[](#step-1-add-hasroles-trait-to-user-model)

```
namespace App\Models;

use Illuminate\Foundation\Auth\User as Authenticatable;
use Ubxty\MultiTenantLaravelPermissions\Traits\HasRoles;

class User extends Authenticatable
{
    use HasRoles;
}
```

#### Step 2: Create roles and permissions

[](#step-2-create-roles-and-permissions)

```
use App\Models\Role;
use App\Models\Permission;

// Create permissions
$permission = Permission::create(['name' => 'edit articles']);

// Create roles (with optional tenant scoping)
$role = Role::create([
    'name' => 'writer',
    'title' => 'Content Writer',
    'tenant_id' => tenant_id(), // Optional: scope to current tenant
]);

// Assign permission to role
$role->givePermissionTo($permission);

// Assign role to user
$user->assignRole('writer');
```

---

Tenancy Deep Dive
-----------------

[](#tenancy-deep-dive)

### Tenancy Configuration Options

[](#tenancy-configuration-options)

The `tenancy` section in your config file controls all tenant behavior:

```
// config/multi-tenant-permissions.php
'tenancy' => [
    /*
    |--------------------------------------------------------------------------
    | Tenant Model
    |--------------------------------------------------------------------------
    | The Eloquent model that represents a tenant (Company, Organization, etc.)
    */
    'tenant_model' => App\Models\Company::class,

    /*
    |--------------------------------------------------------------------------
    | Foreign Key
    |--------------------------------------------------------------------------
    | The foreign key column used in tenant-scoped models
    | e.g., 'tenant_id', 'company_id', 'organization_id'
    */
    'tenant_foreign_key' => 'company_id',

    /*
    |--------------------------------------------------------------------------
    | Identification Columns
    |--------------------------------------------------------------------------
    | Columns used to identify tenants from URLs
    */
    'subdomain_column' => 'subdomain',    // For subdomain.yourapp.com
    'domain_column' => 'domain',          // For full domain identification
    'identifier_column' => 'subdomain',   // Primary identifier

    /*
    |--------------------------------------------------------------------------
    | Domain Settings
    |--------------------------------------------------------------------------
    */
    'domain_identification' => false,     // Enable full domain mode (tenant.com)
    'central_domains' => [                // Domains that don't resolve a tenant
        'yourapp.com',
        'www.yourapp.com',
        'localhost',
    ],
    'ignored_subdomains' => ['www', 'api', 'app', 'admin'],

    /*
    |--------------------------------------------------------------------------
    | Path Exclusions
    |--------------------------------------------------------------------------
    | URL paths that skip tenant resolution (OAuth callbacks, webhooks)
    */
    'excluded_paths' => [
        'oauth/callback/*',
        'webhooks/*',
        'api/health',
    ],

    /*
    |--------------------------------------------------------------------------
    | Security Settings
    |--------------------------------------------------------------------------
    */
    'abort_without_tenant' => false,       // 404 if no tenant on non-central domain
    'no_tenant_message' => 'Organization not found',
    'validate_user_tenant' => true,        // Ensure user belongs to resolved tenant
    'tenant_mismatch_action' => 'logout',  // 'logout', 'abort', 'redirect'
    'hide_data_without_tenant' => true,    // Return empty results if no tenant

    /*
    |--------------------------------------------------------------------------
    | Super Admin
    |--------------------------------------------------------------------------
    */
    'super_admin_role' => 'super_admin',   // Role that bypasses tenant restrictions

    /*
    |--------------------------------------------------------------------------
    | URL Configuration
    |--------------------------------------------------------------------------
    | Auto-configure app URL based on tenant subdomain
    */
    'configure_urls' => false,
    'url_scheme' => 'https',
    'base_domain' => env('APP_BASE_DOMAIN', 'yourapp.com'),

    /*
    |--------------------------------------------------------------------------
    | Debug
    |--------------------------------------------------------------------------
    */
    'debug_logging' => env('TENANCY_DEBUG', false),
],
```

---

### Middleware Reference

[](#middleware-reference)

MiddlewareAliasDescriptionUse Case`SetTenantFromHost``tenant.host`Resolves tenant from domain/subdomainWeb routes`SetTenantFromUser``tenant.user`Resolves tenant from authenticated userAPI routes`EnsureTenant``tenant.ensure`Aborts 403 if no tenant resolvedProtected routes`SkipTenantResolution``tenant.skip`Bypasses tenant resolutionOAuth, webhooks#### Laravel 11+ Setup (bootstrap/app.php)

[](#laravel-11-setup-bootstrapappphp)

```
use Illuminate\Foundation\Configuration\Middleware;

return Application::configure(basePath: dirname(__DIR__))
    ->withMiddleware(function (Middleware $middleware) {
        // Web: Resolve from subdomain
        $middleware->web(prepend: [
            \Ubxty\MultiTenantLaravelPermissions\Tenancy\Middleware\SetTenantFromHost::class,
        ]);

        // API: Resolve from authenticated user
        $middleware->api(append: [
            \Ubxty\MultiTenantLaravelPermissions\Tenancy\Middleware\SetTenantFromUser::class,
        ]);

        // Register aliases for route-level usage
        $middleware->alias([
            'tenant.host' => \Ubxty\MultiTenantLaravelPermissions\Tenancy\Middleware\SetTenantFromHost::class,
            'tenant.user' => \Ubxty\MultiTenantLaravelPermissions\Tenancy\Middleware\SetTenantFromUser::class,
            'tenant.ensure' => \Ubxty\MultiTenantLaravelPermissions\Tenancy\Middleware\EnsureTenant::class,
            'tenant.skip' => \Ubxty\MultiTenantLaravelPermissions\Tenancy\Middleware\SkipTenantResolution::class,
        ]);
    });
```

#### Laravel 10 Setup (app/Http/Kernel.php)

[](#laravel-10-setup-apphttpkernelphp)

```
protected $middlewareGroups = [
    'web' => [
        \Ubxty\MultiTenantLaravelPermissions\Tenancy\Middleware\SetTenantFromHost::class,
        // ... other middleware
    ],
    'api' => [
        \Ubxty\MultiTenantLaravelPermissions\Tenancy\Middleware\SetTenantFromUser::class,
        // ... other middleware
    ],
];

protected $middlewareAliases = [
    'tenant.host' => \Ubxty\MultiTenantLaravelPermissions\Tenancy\Middleware\SetTenantFromHost::class,
    'tenant.user' => \Ubxty\MultiTenantLaravelPermissions\Tenancy\Middleware\SetTenantFromUser::class,
    'tenant.ensure' => \Ubxty\MultiTenantLaravelPermissions\Tenancy\Middleware\EnsureTenant::class,
    'tenant.skip' => \Ubxty\MultiTenantLaravelPermissions\Tenancy\Middleware\SkipTenantResolution::class,
];
```

#### Route-Level Usage

[](#route-level-usage)

```
// Require tenant context
Route::middleware(['tenant.ensure'])->group(function () {
    Route::get('/dashboard', [DashboardController::class, 'index']);
    Route::resource('projects', ProjectController::class);
});

// Skip tenant resolution (OAuth, webhooks)
Route::middleware(['tenant.skip'])->group(function () {
    Route::get('/oauth/callback/{provider}', [OAuthController::class, 'callback']);
    Route::post('/webhooks/stripe', [StripeWebhookController::class, 'handle']);
});

// Mix: require auth + tenant
Route::middleware(['auth', 'tenant.ensure'])->group(function () {
    Route::get('/profile', [ProfileController::class, 'show']);
});
```

---

### Model Scoping

[](#model-scoping)

#### The TenantScoped Trait

[](#the-tenantscoped-trait)

Add to any model that should be filtered by tenant:

```
namespace App\Models;

use Illuminate\Database\Eloquent\Model;
use Ubxty\MultiTenantLaravelPermissions\Tenancy\Traits\TenantScoped;

class Project extends Model
{
    use TenantScoped;

    protected $fillable = ['name', 'description', 'company_id'];
}
```

**What it does automatically:**

1. **Filters all queries** - `Project::all()` only returns projects for current tenant
2. **Sets tenant on create** - `Project::create([...])` auto-sets `company_id`
3. **Hides data without tenant** - If no tenant context, returns empty (security)

#### Bypassing the Scope

[](#bypassing-the-scope)

```
// Get all records across all tenants (admin panels, reports)
$allProjects = Project::withoutTenantScope()->get();

// Query for a specific tenant
$projects = Project::forTenant($tenantId)->get();

// Check if model belongs to current tenant
if ($project->belongsToCurrentTenant()) {
    // ...
}
```

#### Custom Foreign Key

[](#custom-foreign-key)

```
class Project extends Model
{
    use TenantScoped;

    // Override if different from config
    public function getTenantForeignKeyName(): string
    {
        return 'organization_id';
    }
}
```

---

### Helper Functions &amp; Facade

[](#helper-functions--facade)

#### Helper Functions

[](#helper-functions)

Available globally after package installation:

```
// Get current tenant model (or null)
$tenant = tenant();

// Get current tenant ID (or null)
$id = tenant_id();

// Check if tenant is set
if (has_tenant()) {
    // In tenant context
}

// Backward compatible alias (for migration from CompanyContext)
$company = company();

// Run code for a specific tenant
$result = run_for_tenant($tenant, function ($tenant) {
    // All queries here are scoped to $tenant
    return Project::count();
});

// Access TenantContext directly
$context = tenancy();
```

#### Tenant Facade

[](#tenant-facade)

```
use Ubxty\MultiTenantLaravelPermissions\Facades\Tenant;

// Get current tenant
$tenant = Tenant::get();

// Get tenant ID
$id = Tenant::id();

// Check if tenant is set
if (Tenant::check()) { ... }

// Run for tenant
Tenant::run($tenant, function ($tenant) {
    return Project::all();
});

// Register booting callback
Tenant::booting(function ($tenant) {
    Log::info("Booting tenant: {$tenant->name}");
});

// Register ending callback
Tenant::ending(function ($tenant) {
    Log::info("Ending tenant: {$tenant->name}");
});
```

---

### Context Switching

[](#context-switching)

Run code within a specific tenant's context:

```
// Using helper
$projects = run_for_tenant($tenant, function ($tenant) {
    // All TenantScoped models query this tenant
    return Project::with('tasks')->get();
});

// Using facade
$count = Tenant::run($tenant, fn($t) => Project::count());

// Using TenantContext directly
use Ubxty\MultiTenantLaravelPermissions\Tenancy\Services\TenantContext;

app(TenantContext::class)->run($tenant, function ($tenant) {
    // ...
});
```

**Note:** Context switching is synchronous. The original tenant is restored after the callback.

---

Permissions &amp; Roles
-----------------------

[](#permissions--roles-1)

### Creating &amp; Managing Roles and Permissions

[](#creating--managing-roles-and-permissions)

```
use App\Models\Role;
use App\Models\Permission;

// Create permissions
Permission::create(['name' => 'view posts']);
Permission::create(['name' => 'create posts']);
Permission::create(['name' => 'edit posts']);
Permission::create(['name' => 'delete posts']);

// Create a role (optionally scoped to tenant)
$role = Role::create([
    'name' => 'editor',
    'title' => 'Content Editor',
    'tenant_id' => tenant_id(), // null for global roles
]);

// Assign permissions to role
$role->givePermissionTo('view posts', 'create posts', 'edit posts');

// Or sync permissions
$role->syncPermissions(['view posts', 'create posts']);

// Remove a permission
$role->revokePermissionTo('delete posts');
```

### Assigning Roles to Users

[](#assigning-roles-to-users)

```
$user = User::find(1);

// Assign a single role
$user->assignRole('editor');

// Assign multiple roles
$user->assignRole('editor', 'writer');

// Sync roles (replaces all current roles)
$user->syncRoles(['editor', 'admin']);

// Remove a role
$user->removeRole('writer');
```

### Checking Permissions

[](#checking-permissions)

```
// Check specific permission
if ($user->hasPermissionTo('edit posts')) { ... }

// Check via role
if ($user->hasRole('admin')) { ... }

// Check any role
if ($user->hasAnyRole(['admin', 'editor'])) { ... }

// Check all roles
if ($user->hasAllRoles(['editor', 'writer'])) { ... }

// Using Laravel's can() - works with Gate
if ($user->can('edit posts')) { ... }
```

### Tenant-Scoped Permissions

[](#tenant-scoped-permissions)

Check permissions for a specific tenant:

```
// Check if user has role for a specific tenant
if ($user->hasRoleForTenant('admin', $tenantId)) { ... }

// Assign a role for a specific tenant
$user->assignRoleForTenant('manager', $tenantId);

// Get all roles for a tenant
$roles = $user->getRolesForTenant($tenantId);

// Sync roles for a tenant
$user->syncRolesForTenant(['editor', 'writer'], $tenantId);

// Check permission for a tenant
if ($user->hasPermissionForTenant('edit posts', $tenantId)) { ... }
```

### Custom Models (Optional)

[](#custom-models-optional)

Extend the package models for custom logic:

```
// App\Models\Role.php
namespace App\Models;

use Ubxty\MultiTenantLaravelPermissions\Models\Role as BaseRole;

class Role extends BaseRole
{
    // Your custom logic
}
```

Update your config to use custom models:

```
// config/multi-tenant-permissions.php
'models' => [
    'permission' => App\Models\Permission::class,
    'role' => App\Models\Role::class,
],
```

---

Wildcard Permissions
--------------------

[](#wildcard-permissions)

Grant access to a group of permissions with wildcards:

```
// Create specific permissions
Permission::create(['name' => 'posts.create']);
Permission::create(['name' => 'posts.edit']);
Permission::create(['name' => 'posts.delete']);
Permission::create(['name' => 'posts.publish']);

// Create wildcard permission
$wildcard = Permission::create(['name' => 'posts.*']);

// Assign wildcard - grants access to ALL posts.* permissions
$role->givePermissionTo($wildcard);

// Now this role has access to all posts permissions
$user->can('posts.create'); // true
$user->can('posts.edit');   // true
$user->can('posts.delete'); // true
$user->can('posts.publish'); // true

// Multi-level wildcards
Permission::create(['name' => 'admin.*']);         // All admin permissions
Permission::create(['name' => 'admin.users.*']);   // All admin user permissions
```

---

Permission Settings &amp; Scopes
--------------------------------

[](#permission-settings--scopes)

Attach JSON settings to permissions when assigning to roles:

```
$role = Role::findByName('manager');

// Grant permission with specific scope settings
$role->givePermissionToWithSettings('view_projects', [
    'scope' => 'team_only',
    'max_count' => 10,
    'regions' => ['us', 'eu'],
]);

// Update settings
$role->updatePermissionSettings('view_projects', [
    'scope' => 'global',
    'max_count' => 100,
]);

// Retrieve settings
$settings = $role->getPermissionSettings('view_projects');
// ['scope' => 'global', 'max_count' => 100, 'regions' => ['us', 'eu']]

// Use in application logic
if ($user->hasPermissionTo('view_projects')) {
    $settings = $user->getPermissionSettings('view_projects');

    if ($settings['scope'] === 'team_only') {
        $projects = Project::where('team_id', $user->team_id)->get();
    }
}
```

---

High-Performance Caching
------------------------

[](#high-performance-caching)

This package implements a dual-layer caching system for optimal performance:

1. **Database JSON Cache**: Permissions and roles are cached as JSON in columns on the users table
2. **In-Memory Cache**: Parsed once per request, all subsequent checks use RAM

### Setup

[](#setup)

Add cache columns to your users table:

```
Schema::table('users', function (Blueprint $table) {
    $table->json('permissions_cache')->nullable();
    $table->json('roles_cache')->nullable();
});
```

### Performance Impact

[](#performance-impact)

ScenarioWithout CacheWith CacheDashboard with 20 permission checks20+ DB queries1 DB queryKanban board with 50 items50+ DB queries1 DB queryAPI endpoint with multiple checksN DB queries0 DB queries\*\*After first request loads cache into memory.

### Cache Configuration

[](#cache-configuration)

```
// config/multi-tenant-permissions.php
'cache' => [
    'expiration_time' => 60 * 24, // minutes
    'key' => 'multi_tenant_permissions.cache',
    'store' => 'default',
],
```

### Cache Invalidation

[](#cache-invalidation)

Cache is automatically cleared when:

- Roles are assigned/removed from user
- Permissions are assigned/removed from role
- Role or permission is deleted

Manual cache clear:

```
php artisan permission:cache-reset
```

---

Middleware
----------

[](#middleware)

### Permission &amp; Role Middleware

[](#permission--role-middleware)

```
// Single role
Route::get('/admin', [AdminController::class, 'index'])
    ->middleware('role:admin');

// Multiple roles (any)
Route::get('/manage', [ManageController::class, 'index'])
    ->middleware('role:admin|manager');

// Single permission
Route::post('/posts', [PostController::class, 'store'])
    ->middleware('permission:create posts');

// Multiple permissions (any)
Route::put('/posts/{id}', [PostController::class, 'update'])
    ->middleware('permission:edit posts|manage posts');

// Role OR permission
Route::delete('/posts/{id}', [PostController::class, 'destroy'])
    ->middleware('role_or_permission:admin|delete posts');
```

### Registering Middleware

[](#registering-middleware)

**Laravel 11+ (bootstrap/app.php):**

```
->withMiddleware(function (Middleware $middleware) {
    $middleware->alias([
        'role' => \Ubxty\MultiTenantLaravelPermissions\Middleware\RoleMiddleware::class,
        'permission' => \Ubxty\MultiTenantLaravelPermissions\Middleware\PermissionMiddleware::class,
        'role_or_permission' => \Ubxty\MultiTenantLaravelPermissions\Middleware\RoleOrPermissionMiddleware::class,
    ]);
})
```

**Laravel 10 (app/Http/Kernel.php):**

```
protected $middlewareAliases = [
    'role' => \Ubxty\MultiTenantLaravelPermissions\Middleware\RoleMiddleware::class,
    'permission' => \Ubxty\MultiTenantLaravelPermissions\Middleware\PermissionMiddleware::class,
    'role_or_permission' => \Ubxty\MultiTenantLaravelPermissions\Middleware\RoleOrPermissionMiddleware::class,
];
```

---

Blade Directives
----------------

[](#blade-directives)

```
{{-- Check single role --}}
@role('admin')
    Admin Panel
@endrole

{{-- Check any role --}}
@hasanyrole('admin|editor')
    Edit Content
@endhasanyrole

{{-- Check all roles --}}
@hasallroles('editor|writer')
    You can both write and edit!
@endhasallroles

{{-- Negation --}}
@unlessrole('admin')
    You need admin access to see more options.
@endunlessrole

{{-- Laravel's native @can works too --}}
@can('edit posts')
    Edit Post
@endcan

@canany(['edit posts', 'delete posts'])
    ...
@endcanany
```

---

Commands
--------

[](#commands)

```
# Clear permission cache for all users
php artisan permission:cache-reset

# Show current permission cache status
php artisan permission:show
```

---

Upgrading from Existing Tenancy
-------------------------------

[](#upgrading-from-existing-tenancy)

If you're migrating from a custom tenancy implementation (like `CompanyContext`), here's how:

### Option 1: Use Aliases (Backward Compatible)

[](#option-1-use-aliases-backward-compatible)

```
// app/Providers/AppServiceProvider.php
public function register()
{
    // Alias the package TenantContext as your old class
    $this->app->alias(
        \Ubxty\MultiTenantLaravelPermissions\Tenancy\Services\TenantContext::class,
        \App\Services\Tenancy\CompanyContext::class
    );
}
```

### Option 2: Search &amp; Replace

[](#option-2-search--replace)

1. Replace imports:

    ```
    use App\Services\Tenancy\CompanyContext;
    →
    use Ubxty\MultiTenantLaravelPermissions\Tenancy\Services\TenantContext;

    ```
2. Replace method calls:

    ```
    // Old
    CompanyContext::get();
    CompanyContext::set($company);

    // New (facade)
    Tenant::get();
    Tenant::set($company);

    // Or helpers
    tenant();
    ```

### Option 3: Use Compatibility Helper

[](#option-3-use-compatibility-helper)

The package includes a `company()` helper for backward compatibility:

```
// These are equivalent:
$company = company();
$company = tenant();
```

---

Configuration
-------------

[](#configuration)

The full configuration file allows customization of:

- **Models**: Custom Role and Permission classes
- **Table Names**: Custom database table names
- **Column Names**: Custom pivot keys, tenant column, cache columns
- **Cache Settings**: Expiration time, cache key, cache store
- **Tenancy**: Full tenancy configuration (see [Tenancy Configuration](#tenancy-configuration-options))

See the published `config/multi-tenant-permissions.php` for all options.

---

Testing
-------

[](#testing)

```
composer test
```

---

Changelog
---------

[](#changelog)

Please see [CHANGELOG](CHANGELOG.md) for more information on what has changed recently.

Contributing
------------

[](#contributing)

Please see [CONTRIBUTING](CONTRIBUTING.md) for details.

Security
--------

[](#security)

If you discover any security related issues, please email  or [open an issue](https://github.com/ubxty/multi-tenant-laravel-permissions/issues) on GitHub.

Credits
-------

[](#credits)

- [Ubxty](https://ubxty.com)
- [All Contributors](../../contributors)

License
-------

[](#license)

The MIT License (MIT). Please see [License File](LICENSE) for more information.

###  Health Score

37

—

LowBetter than 83% of packages

Maintenance80

Actively maintained with recent releases

Popularity6

Limited adoption so far

Community6

Small or concentrated contributor base

Maturity48

Maturing project, gaining track record

 Bus Factor1

Top contributor holds 100% of commits — single point of failure

How is this calculated?**Maintenance (25%)** — Last commit recency, latest release date, and issue-to-star ratio. Uses a 2-year decay window.

**Popularity (30%)** — Total and monthly downloads, GitHub stars, and forks. Logarithmic scaling prevents top-heavy scores.

**Community (15%)** — Contributors, dependents, forks, watchers, and maintainers. Measures real ecosystem engagement.

**Maturity (30%)** — Project age, version count, PHP version support, and release stability.

###  Release Activity

Cadence

Every ~4 days

Total

2

Last Release

105d ago

Major Versions

v1.0.0 → 2.0.02026-01-27

### Community

Maintainers

![](https://www.gravatar.com/avatar/e2de529037b10593205d6c41aa1d5331ef027e398489b74355b6d222ac10f2f1?d=identicon)[ubxty](/maintainers/ubxty)

---

Top Contributors

[![ravdeepsingh22](https://avatars.githubusercontent.com/u/13014642?v=4)](https://github.com/ravdeepsingh22 "ravdeepsingh22 (6 commits)")

---

Tags

laravelsecurityauthorizationaclrolespermissionsrbacsubdomainsaasmulti-tenantaccess-controltenancy

###  Code Quality

TestsPHPUnit

### Embed Badge

![Health badge](/badges/ubxty-multi-tenant-laravel-permissions/health.svg)

```
[![Health](https://phpackages.com/badges/ubxty-multi-tenant-laravel-permissions/health.svg)](https://phpackages.com/packages/ubxty-multi-tenant-laravel-permissions)
```

###  Alternatives

[spatie/laravel-permission

Permission handling for Laravel 12 and up

12.9k89.8M1.0k](/packages/spatie-laravel-permission)[bezhansalleh/filament-shield

Filament support for `spatie/laravel-permission`.

2.8k2.9M88](/packages/bezhansalleh-filament-shield)[wnikk/laravel-access-rules

Simple system of ACR (access control rules) for Laravel, with roles, groups, unlimited inheritance and possibility of multiplayer use.

103.6k1](/packages/wnikk-laravel-access-rules)[hasinhayder/tyro

Tyro - The ultimate Authentication, Authorization, and Role &amp; Privilege Management solution for Laravel 12 &amp; 13

6712.1k2](/packages/hasinhayder-tyro)[erag/laravel-role-permission

A simple and easy-to-install role and permission management package for Laravel, supporting versions 10.x and 11.x

404.2k](/packages/erag-laravel-role-permission)[hosseinhezami/laravel-permission-manager

Advanced permission manager for Laravel.

403.3k](/packages/hosseinhezami-laravel-permission-manager)

PHPackages © 2026

[Directory](/)[Categories](/categories)[Trending](/trending)[Changelog](/changelog)[Analyze](/analyze)
