PHPackages                             hdaklue/porter - 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. hdaklue/porter

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

hdaklue/porter
==============

Ultra-Minimal Laravel Role Management - Your application's trusted doorkeeper

2.0.12(6mo ago)121956↓50%4[1 PRs](https://github.com/hdaklue/porter/pulls)MITPHPPHP ^8.2CI failing

Since Sep 2Pushed 6mo ago1 watchersCompare

[ Source](https://github.com/hdaklue/porter)[ Packagist](https://packagist.org/packages/hdaklue/porter)[ RSS](/packages/hdaklue-porter/feed)WikiDiscussions master Synced 1mo ago

READMEChangelog (10)Dependencies (10)Versions (59)Used By (0)

Porter - Lightweight Access Control Management yet 💪 for Laravel
================================================================

[](#porter---lightweight-access-control-management-yet--for-laravel)

[![Tests](https://github.com/hdaklue/porter/actions/workflows/ci.yml/badge.svg)](https://github.com/hdaklue/porter/actions/workflows/ci.yml)[![PHP Version](https://camo.githubusercontent.com/ef0054230522e542bc1f908ac005c6c75888dea255bac910f9015e12095e31d7/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f7068702d253545382e332d626c7565)](https://packagist.org/packages/hdaklue/porter)[![Laravel](https://camo.githubusercontent.com/5194196ab0b8591499096f3de97eb81b1397fc2be5663e7c21b9a179613d52ea/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f6c61726176656c2d25354531312e3025323025374325323025354531322e302d726564)](https://packagist.org/packages/hdaklue/porter)[![License](https://camo.githubusercontent.com/f8df3091bbe1149f398a5369b2c39e896766f9f6efba3477c63e9b4aa940ef14/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f6c6963656e73652d4d49542d677265656e)](LICENSE)

**Your application's trusted doorkeeper** 🚪

A lightweight, blazing-fast Laravel access control package that treats roles as what they truly are: **domain business logic**, not database abstractions. Built for developers who value simplicity, performance, and clean architecture.

**🚀 Enterprise-Ready**: Porter uniquely supports cross-database role assignments, making it perfect for complex multi-database architectures, microservices, and distributed systems where role data lives on different database connections than your business models.

**Porter's Core Concept**: Any model can be **Assignable** (users, teams, departments), any model can be **Roleable** (projects, organizations, documents), and the **Roster** defines the access control relationship between them. This flexibility lets you model complex business scenarios with simple, expressive code.

**Perfect for**: [Team collaboration](#team-collaboration-platform), [SaaS feature consumption](#saas-feature-consumption), [document management](#document-management-system), [project access control](#project-management-system), [multi-tenant applications](#multi-tenant-application), [enterprise hierarchies](#enterprise-hierarchy-management), and [cross-database architectures](#cross-database-support).

🎥 Video Demos Needed
--------------------

[](#-video-demos-needed)

Porter is seeking a co-maintainer to create video demos and tutorials showcasing the package features and usage. If you're interested in creating educational content for this Laravel package, please contact

---

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

[](#table-of-contents)

- [Why Porter?](#why-porter)
- [Roadmap &amp; Community Input](#roadmap--community-input)
- [Core Features](#core-features) • **[Complete Guide →](docs/core-features.md)**
- [Suggested Usage](#suggested-usage) • **[Complete Guide →](docs/suggested-usage.md)**
- [Installation](#installation)
- [Advanced Features](#advanced-features)
- [Multitenancy](#multitenancy) • **[Complete Guide →](docs/multitenancy.md)**
- [Cross-Database Architecture](#cross-database-architecture)
- [Configuration](#configuration)
- [Laravel Integration](#laravel-integration) • **[Complete Guide →](docs/laravel-integration.md)**
- [Migration Strategy](#migration-strategy)
- [Performance](#performance)
- [CLI Commands](#cli-commands)
- [Testing](#testing)
- [Requirements](#requirements)
- [Contributing](#contributing)
- [License](#license)

---

Why Porter?
-----------

[](#why-porter)

> *"Roles are business logic, not database magic."*

Porter was born from the frustration of dealing with bloated RBAC packages that turn simple role assignments into complex database gymnastics. As a **fresh package** entering the Laravel ecosystem, Porter aims to solve real problems that developers face daily. I believe in providing a solution that is both powerful and elegant, convincing the community that there's a better way to handle role management.

### The Problem with Existing Solutions

[](#the-problem-with-existing-solutions)

Most RBAC packages are:

- **Over-engineered** - 30+ classes for simple role assignments
- **Database-heavy** - Complex joins and foreign key nightmares
- **Performance-blind** - Slow queries that don't scale
- **Generic** - One-size-fits-all approaches that fit no one

### Porter's Approach

[](#porters-approach)

Porter treats roles as **business assignments** - contextual relationships between users and entities, not generic database records. Each role assignment carries business logic and domain knowledge.

**🌐 Enterprise-Ready:** Porter includes sophisticated cross-database support, automatically handling scenarios where your models, RBAC data, and business logic span multiple database connections - perfect for multi-tenant SaaS, microservices, and enterprise architectures.

**Enterprise Architecture Support**: Porter automatically handles cross-database scenarios where your Roster model lives on a different database connection than your application models. This sophisticated capability enables complex enterprise architectures while maintaining Porter's signature simplicity.

### Porter vs Database-Heavy Approaches

[](#porter-vs-database-heavy-approaches)

Common question: *"Why not use traditional database-based access control?"*

FeatureDatabase-Heavy SystemsPorter Access Control**Assignment Model**Fixed user-permission mappingsFlexible Assignable-Roleable-Roster pattern**Entity Support**Limited to users and rolesAny model as Assignable or Roleable**Role Concept**Generic database recordsBusiness assignments with context**Assignment Logic**Database foreign keysPHP class methods with business rules**Entity Context**Global permissionsEntity-specific assignments**Type Safety**String-basedFull PHP type safety**Business Logic**Scattered across codebaseEncapsulated in role classes**IDE Support**LimitedFull autocomplete**Performance**Multiple DB queriesSingle table, memory checks**Cross-Database Support**LimitedAutomatic detection &amp; fallback strategies**Enterprise Architecture**BasicSophisticated multi-database handling**Use Traditional Systems if:** You need complex global permission matrices
**Use Porter Access Control if:** You need flexible entity-specific assignments with type safety and simplicity

### **Porter's Sweet Spot:**

[](#porters-sweet-spot)

- **SaaS applications** with fixed role structures
- **Enterprise applications** with well-defined hierarchies
- **Microservices** with service-specific roles
- **High-performance** applications where DB queries are a bottleneck
- **Multi-database architectures** requiring cross-connection role assignments
- **Distributed systems** where roles span multiple data sources
- **Complex enterprise environments** with segregated database strategies

**✅ Multitenancy Support:** Porter now includes optional multitenancy features with tenant-aware role assignments, tenant integrity validation, and support for tenant entities as roleables - perfect for SaaS applications and enterprise multi-tenant architectures.

---

Roadmap &amp; Community Input
-----------------------------

[](#roadmap--community-input)

As a **new package** your feedback directly shapes Porter's future! I am actively seeking community input and suggestions to prioritize features and ensure Porter evolves into the most valuable tool for your Laravel app.

### 🎯 **Potential Features (Vote &amp; Discuss!)**

[](#-potential-features-vote--discuss)

#### 🔒 **Assignment Constraints &amp; Actions**

[](#-assignment-constraints--actions)

Advanced assignment rules with contextual validation and conditional actions.

**Benefits:**

- 🎯 Conditional role assignments based on business rules
- ⏰ Time-based assignment expiration
- 📊 Assignment quotas and limits
- 🔄 Automatic assignment workflows
- 🧮 Assignment validation with custom constraints

#### 🌐 **REST API Endpoints**

[](#-rest-api-endpoints)

Ready-to-use API endpoints for role management.

**Benefits:**

- 📱 Mobile app integration
- 🔗 Third-party service connectivity
- ⚡ Frontend SPA support
- 📊 External dashboard integration

### 🗳️ **Help Me Decide!**

[](#️-help-me-decide)

I want to build what YOU need most. Please share your feedback on:

1. **Which feature would have the biggest impact on your projects?**
2. **What specific use cases do you have in mind?**
3. **Are there other features not listed that would be valuable?**
4. **What's the best way for the community to provide ongoing feedback?**

#### 💬 **Community Feedback Options:**

[](#-community-feedback-options)

We welcome your feedback! Please use:

- **GitHub Discussions** for ongoing feature conversations.
- **Project Wiki** for collaborative roadmap planning.
- **GitHub Issues** for bug reports and feature requests.

#### 🎖️ **Recognition**

[](#️-recognition)

Contributors who provide valuable feedback will be:

- 📜 **Credited** in release notes
- 🏷️ **Mentioned** as community advisors
- 🚀 **Early access** to beta features
- 💬 **Direct input** on API design decisions

---

Core Features
-------------

[](#core-features)

- 🎯 **Assignment-Focused Design**: Treats roles as business assignments with contextual logic
- 🏗️ **Individual Role Classes**: Each role is its own focused class extending `BaseRole`
- 🚀 **Ultra-Minimal Architecture**: Just 3 core components for assignment management
- 🔥 **Blazing Performance**: Optimized for speed with minimal database interaction, built-in caching, and intelligent cross-database query optimization
- 🌐 **Cross-Database Support**: Enterprise-grade multi-database architecture with automatic connection detection
- 🏢 **Multitenancy Support**: Optional tenant-aware role assignments with integrity validation and self-reference support
- 🔒 **Enhanced Security**: Assignment keys encrypted with Laravel's built-in encryption
- 🎯 **Automatic RoleCast**: Seamless conversion between database keys and type-safe RoleContract instances
- 🏢 **Cross-Database Intelligence**: Automatic detection and seamless handling of multi-database architectures
- 🎨 **Perfect Laravel Integration**: Custom Blade directives, middleware, plus seamless Gates and Policies

**🔗 [Complete Core Features Guide →](docs/core-features.md)**

Learn about individual role classes, ultra-minimal architecture, blazing performance optimizations, latest features, and perfect Laravel integration.

---

Real-World Use Cases
--------------------

[](#real-world-use-cases)

Porter's flexible Assignable-Roleable-Roster pattern adapts to diverse business scenarios:

### Team Collaboration Platform

[](#team-collaboration-platform)

```
// Teams (Assignable) can have roles on Projects (Roleable)
Porter::assign($developmentTeam, $mobileApp, 'lead_developer');
Porter::assign($designTeam, $mobileApp, 'ui_designer');
Porter::assign($qaTeam, $mobileApp, 'tester');

// Business role with domain logic
final class LeadDeveloper extends BaseRole
{
    public function getName(): string { return 'lead_developer'; }
    public function getLevel(): int { return 8; }

    public function canAssignTasks(): bool { return true; }
    public function canMergeCode(): bool { return true; }
    public function getMaxTeamSize(): int { return 12; }
    public function canApproveDeployment(string $environment): bool {
        return in_array($environment, ['staging', 'production']);
    }
}

// Usage through assignment
if ($user->getAssignmentOn($project)->canAssignTasks()) {
    // Allow task assignment
}

if ($user->getAssignmentOn($project)->canApproveDeployment('production')) {
    // Allow production deployment
}
```

### SaaS Feature Consumption

[](#saas-feature-consumption)

```
// Organizations (Assignable) get feature access on Subscriptions (Roleable)
Porter::assign($organization, $premiumSubscription, 'analytics_access');
Porter::assign($organization, $premiumSubscription, 'api_access');
Porter::assign($organization, $enterpriseSubscription, 'white_label');

// Business role with consumption limits
final class AnalyticsAccess extends BaseRole
{
    public function getName(): string { return 'analytics_access'; }
    public function getLevel(): int { return 3; }

    public function getMaxReports(): int { return 50; }
    public function canExportData(): bool { return true; }
    public function getRetentionDays(): int { return 90; }
    public function canGenerateReport(int $count): bool {
        return $count getMaxReports();
    }
}

// Usage through assignment
if ($organization->getAssignmentOn($subscription)->canExportData()) {
    // Enable data export feature
}

if ($organization->getAssignmentOn($subscription)->canGenerateReport($requestedCount)) {
    // Generate analytics reports within limits
}
```

### Document Management System

[](#document-management-system)

```
// Users/Departments (Assignable) have roles on Documents/Folders (Roleable)
Porter::assign($user, $confidentialDocument, 'viewer');
Porter::assign($legalDepartment, $contractsFolder, 'editor');
Porter::assign($hrTeam, $personnelFolder, 'admin');

// Business role with document constraints
final class DocumentEditor extends BaseRole
{
    public function getName(): string { return 'editor'; }
    public function getLevel(): int { return 5; }

    public function canEdit(string $fileType): bool {
        return in_array($fileType, ['pdf', 'docx', 'txt']);
    }

    public function canUpload(int $fileSize): bool {
        return $fileSize getAssignmentOn($document)->canEdit($document->type)) {
    // Allow document editing
}

if ($user->getAssignmentOn($folder)->canUpload($uploadFile->size)) {
    // Allow file upload within size limits
}
```

### Project Management System

[](#project-management-system)

```
// Multiple assignment types for complex project structures
Porter::assign($developer, $project, 'contributor');
Porter::assign($clientCompany, $project, 'stakeholder');
Porter::assign($vendorTeam, $project, 'external_consultant');

// Business role with project permissions
final class ProjectStakeholder extends BaseRole
{
    public function getName(): string { return 'stakeholder'; }
    public function getLevel(): int { return 6; }

    public function canViewReports(): bool { return true; }
    public function canRequestChange(int $cost): bool {
        return $cost getAssignmentOn($project)->canRequestChange($changeRequest->cost)) {
    // Process stakeholder change request
}

if ($client->getAssignmentOn($project)->canAccessMilestone('delivery')) {
    // Allow access to delivery milestone
}
```

### Multi-Tenant Application

[](#multi-tenant-application)

```
// Users (Assignable) have roles on Tenants/Workspaces (Roleable)
Porter::assign($user, $workspace, 'admin');
Porter::assign($user, $anotherWorkspace, 'member');

// Business role with tenant permissions
final class WorkspaceAdmin extends BaseRole
{
    public function getName(): string { return 'admin'; }
    public function getLevel(): int { return 9; }

    public function canManageUsers(): bool { return true; }
    public function canConfigureIntegrations(): bool { return true; }
    public function canAccessBilling(): bool { return true; }
    public function getMaxSeats(): int { return 100; }
}

// Usage through assignment
if ($user->getAssignmentOn($workspace)->canManageUsers()) {
    // Allow user management in this workspace
}

if ($user->getAssignmentOn($workspace)->canAccessBilling()) {
    // Show billing settings for this workspace only
}
```

### Enterprise Hierarchy Management

[](#enterprise-hierarchy-management)

```
// Departments (Assignable) have roles on Divisions/Subsidiaries (Roleable)
Porter::assign($financeTeam, $subsidiary, 'budget_approver');
Porter::assign($auditDepartment, $division, 'compliance_reviewer');
Porter::assign($executiveTeam, $corporation, 'strategic_decision_maker');

// Business role with enterprise constraints
final class BudgetApprover extends BaseRole
{
    public function getName(): string { return 'budget_approver'; }
    public function getLevel(): int { return 7; }

    public function canApprove(int $amount): bool {
        return $amount getAssignmentOn($subsidiary)->canApprove($budgetRequest->amount)) {
    // Process budget approval
}

if ($financeTeam->getAssignmentOn($subsidiary)->canApproveInRegion($request->region)) {
    // Allow regional budget approval
}
```

---

Suggested Usage
---------------

[](#suggested-usage)

### Quick Start

[](#quick-start)

```
use Hdaklue\Porter\Facades\Porter;
use App\Porter\{Admin, Editor};

// Basic role operations - accepts both strings and RoleContract objects
Porter::assign($user, $project, 'admin');           // String
Porter::assign($user, $project, new Admin());       // RoleContract object

$isAdmin = $user->hasRoleOn($project, 'admin');     // String
$isAdmin = $user->hasRoleOn($project, new Admin()); // RoleContract object

Porter::changeRoleOn($user, $project, new Editor());
```

### Create Role Classes

[](#create-role-classes)

```
# Interactive role creation with guided setup
php artisan porter:create

# Create specific role with description
php artisan porter:create ProjectManager --description="Manages development projects"
```

```
// Use the dynamic role factory with magic methods
use Hdaklue\Porter\RoleFactory;

$admin = RoleFactory::admin();           // Creates Admin role instance
$manager = RoleFactory::projectManager(); // Creates ProjectManager role instance
$editor = RoleFactory::make('editor');    // Creates role by name/key
```

**🔗 [Complete Usage Guide →](docs/suggested-usage.md)**

Learn about role creation methods, real-world examples (SaaS, E-commerce, Healthcare), advanced patterns, testing strategies, and configuration best practices.

---

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

[](#installation)

```
composer require hdaklue/porter
```

**Flexible installation** with automatic setup:

```
# Basic installation - creates Porter directory with BaseRole only
php artisan porter:install

# Full installation - includes 6 default role classes with proper hierarchy
php artisan porter:install --roles
```

The install command: ✅ Publishes configuration file ✅ Publishes and runs migrations ✅ Creates Porter directory ✅ Optionally creates 6 default role classes (Admin, Manager, Editor, Contributor, Viewer, Guest) ✅ Provides contextual next-step guidance ✅ Blocks installation in production environment for safety

---

Multitenancy
------------

[](#multitenancy)

Porter includes **optional multitenancy support** for SaaS applications and enterprise multi-tenant architectures. When enabled, Porter provides tenant-aware role assignments with integrity validation and flexible tenant entity patterns.

### Key Features

[](#key-features)

- 🏢 **Optional Configuration**: Enable multitenancy via config when needed
- 📋 **Contract-Based Design**: Clear interfaces requiring explicit implementation (no confusing default behaviors)
- 🔒 **Smart Tenant Validation**: Flexible validation allowing cross-tenant assignments for tenant entities and tenant participants
- 🔄 **Self-Reference Support**: Tenant entities can be roleables (users can have roles on their own tenant)
- 🤝 **Participant-Based Access**: Users with roles in a tenant can be assigned to any entity within that tenant
- 🗂️ **Flexible Tenant Keys**: Support for various tenant identifier types (string, uuid, ulid, integer)
- ⚡ **Cache Isolation**: Tenant-specific caching for optimal performance
- 🧼 **Bulk Operations**: Efficient tenant cleanup with `destroyTenantRoles` method

### Quick Example

[](#quick-example)

```
// 1. Enable in config
'multitenancy' => [
    'enabled' => true,
    'tenant_key_type' => 'string',
    'auto_scope' => true,
],

// 2. Implement contracts in your models
class User extends Model implements AssignableEntity, PorterAssignableContract {
    public function getCurrentTenantKey(): ?string {
        return $this->tenant_id;
    }
}

// 3. Tenant-aware assignments work automatically
Porter::assign($user, $project, 'admin'); // Validates tenant context automatically

// 4. Self-reference: Tenant as roleable
Porter::assign($user, $tenant, 'owner'); // User owns their tenant

// 5. Bulk tenant cleanup
Porter::destroyTenantRoles('tenant_123'); // Removes all roles for tenant
```

**🔗 [Complete Multitenancy Guide →](docs/multitenancy.md)**

Learn about configuration, tenant patterns, validation rules, self-reference scenarios, and advanced multitenancy architectures.

---

Advanced Features
-----------------

[](#advanced-features)

### 🌐 Cross-Database Support

[](#-cross-database-support)

Porter includes **enterprise-grade cross-database support** for complex multi-tenant and distributed architectures. The package automatically detects when your models are on different database connections and adapts its queries accordingly.

```
// Multi-database configuration
// config/porter.php
'database_connection' => env('PORTER_DB_CONNECTION', 'tenant_shared'),

// .env configuration examples:
PORTER_DB_CONNECTION=tenant_shared    // Shared tenant database
PORTER_DB_CONNECTION=analytics_db     // Separate analytics database
PORTER_DB_CONNECTION=audit_db         // Compliance/audit database
```

#### Automatic Connection Detection

[](#automatic-connection-detection)

Porter's scopes intelligently handle cross-database scenarios:

```
// Your User model on 'mysql' connection
class User extends Authenticatable
{
    use CanBeAssignedToEntity;
    protected $connection = 'mysql';
}

// Your Project model on 'tenant_mysql' connection
class Project extends Model
{
    use ReceivesRoleAssignments;
    protected $connection = 'tenant_mysql';
}

// Porter's Roster on 'shared_rbac' connection (via config)
// config/porter.php: 'database_connection' => 'shared_rbac'

// These queries work seamlessly across all three databases:
$projects = Project::withAssignmentsTo($user)->get();           // Cross-DB query
$users = User::assignedTo($project)->get();                     // Cross-DB query
$adminProjects = Project::withRole(new Admin())->get();         // Cross-DB query
```

#### Performance-Optimized Cross-Database Queries

[](#performance-optimized-cross-database-queries)

When databases differ, Porter automatically switches to optimized direct queries:

```
// Same database: Uses efficient Eloquent relationships
Project::whereHas('roleAssignments', function($q) use ($user) {
    $q->where('assignable_id', $user->id);
    $q->where('assignable_type', User::class);
})->get();

// Cross-database: Uses optimized direct queries + whereIn
$assignedProjectIds = Roster::where('assignable_id', $user->id)
    ->where('assignable_type', User::class)
    ->where('roleable_type', Project::class)
    ->pluck('roleable_id');

Project::whereIn('id', $assignedProjectIds)->get();
```

#### Cross-Database Use Cases

[](#cross-database-use-cases)

**Multi-Tenant SaaS Architecture:**

```
// Users in shared database, tenant data in separate databases
// RBAC assignments in dedicated security database
$tenantProjects = Project::withAssignmentsTo($user)
    ->where('tenant_id', $currentTenant->id)
    ->get();
```

**Enterprise Service Architecture:**

```
// Users in HR system, projects in project management system
// Role assignments in shared access control system
$accessibleProjects = Project::withRole(new ProjectManager())
    ->where('department_id', $user->department_id)
    ->get();
```

**Compliance &amp; Audit Requirements:**

```
// Main application database + separate audit/compliance database for RBAC
// Ensures role assignments are immutable and separately tracked
config(['porter.database_connection' => 'compliance_db']);
```

### Role Hierarchy &amp; Smart Comparisons

[](#role-hierarchy--smart-comparisons)

```
use App\Porter\{Admin, ProjectManager, Developer, Viewer};

$admin = new Admin();           // Level 10
$manager = new ProjectManager(); // Level 7
$developer = new Developer();    // Level 3
$viewer = new Viewer();         // Level 1

// Intelligent role comparisons
$admin->isHigherThan($manager);     // true
$manager->isHigherThan($developer); // true
$developer->isLowerThan($admin);    // true
$admin->equals(new Admin());        // true

// Business logic in your controllers
public function canManageProject(User $user, Project $project): bool
{
    $userRole = Porter::getRoleOn($user, $project);
    $requiredRole = new ProjectManager();

    return $userRole && $userRole->isHigherThanOrEqual($requiredRole);
}
```

### Enhanced Roster Model with RoleCast &amp; Scopes

[](#enhanced-roster-model-with-rolecast--scopes)

Porter includes an **automatic RoleCast** that seamlessly converts between encrypted database keys and strongly-typed RoleContract instances:

```
use Hdaklue\Porter\Models\Roster;

// Create assignments (accepts both RoleContract instances and strings)
$roster = Roster::create([
    'assignable_type' => User::class,
    'assignable_id' => $user->id,
    'roleable_type' => Project::class,
    'roleable_id' => $project->id,
    'role_key' => new Admin(), // RoleContract instance - automatically converted
]);

// Access role attributes directly (automatically cast to RoleContract)
echo $roster->role_key->getName();    // 'admin'
echo $roster->role_key->getLevel();   // 10
echo $roster->role_key->getLabel();   // 'Administrator'

// Get raw database key when needed
$encryptedKey = $roster->getRoleDBKey(); // Returns encrypted string for queries

// Query role assignments with intelligent scopes
$userAssignments = Roster::forAssignable(User::class, $user->id)->get();
$projectRoles = Roster::forRoleable(Project::class, $project->id)->get();
$adminAssignments = Roster::withRoleName('admin')->get();

// Business logic with type safety
foreach ($assignments as $assignment) {
    if ($assignment->role_key->getLevel() >= 5) {
        // High-level role access
    }

    echo $assignment->description;
    // Output: "User #123 has role 'admin' on Project #456"
}
```

**RoleCast Benefits:**

- 🔒 **Secure Storage**: Role keys encrypted in database (64-char limit)
- 🎯 **Type Safety**: Automatic conversion to RoleContract instances
- 🚀 **Performance**: Leverages existing RoleFactory for efficient role resolution
- 👨‍💻 **Developer Experience**: Work with objects instead of strings

### Custom Role Classes with Business Logic

[](#custom-role-classes-with-business-logic)

```
final class RegionalManager extends BaseRole
{
    public function getName(): string { return 'regional_manager'; }
    public function getLevel(): int { return 8; }

    public function getRegions(): array
    {
        return ['north', 'south', 'east', 'west'];
    }

    public function canAccessRegion(string $region): bool
    {
        return in_array($region, $this->getRegions());
    }

    public function getMaxBudgetApproval(): int
    {
        return 100000; // $100k approval limit
    }
}

// Usage in business logic
if ($user->hasRoleOn($company, new RegionalManager())) {
    $role = Porter::getRoleOn($user, $company);

    if ($role->canAccessRegion('north') && $budget getMaxBudgetApproval()) {
        // Approve the budget for northern region
    }
}
```

---

Cross-Database Architecture
---------------------------

[](#cross-database-architecture)

> *"Porter intelligently handles complex database architectures without sacrificing simplicity."*

Porter's sophisticated cross-database support automatically detects when your Roster model uses a different database connection than your application models, seamlessly adapting its query strategies for optimal performance.

### Enterprise Multi-Database Scenarios

[](#enterprise-multi-database-scenarios)

**Scenario 1: Centralized Role Management**

```
// Your main application database
DB_CONNECTION=mysql_main
DB_HOST=app-db.company.com

// Centralized role/permissions database
PORTER_DB_CONNECTION=mysql_roles
```

**Scenario 2: Microservice Architecture**

```
// User service database
USER_DB_CONNECTION=postgres_users

// Role service database (shared across services)
PORTER_DB_CONNECTION=postgres_roles

// Product service database
PRODUCT_DB_CONNECTION=postgres_products
```

**Scenario 3: Data Sovereignty &amp; Compliance**

```
// EU user data (GDPR compliant)
MAIN_DB_CONNECTION=mysql_eu

// Global role assignments (compliance-neutral)
PORTER_DB_CONNECTION=mysql_global
```

### Automatic Query Strategy Detection

[](#automatic-query-strategy-detection)

Porter automatically optimizes queries based on database connection analysis:

```
// When databases differ, Porter uses direct queries for performance
class User extends Model
{
    use CanBeAssignedToEntity;
    protected $connection = 'mysql_users';
}

class Project extends Model
{
    use ReceivesRoleAssignments;
    protected $connection = 'postgres_projects';
}

// Porter detects different connections and optimizes automatically
$projects = Project::withAssignmentsTo($user)->get();
// Executes: Direct query strategy with whereIn optimization

// When same database, uses standard Eloquent relationships
$projects = Project::withRole(new Admin())->get();
// Executes: Standard whereHas with join optimization
```

### Cross-Database Performance Benefits

[](#cross-database-performance-benefits)

ScenarioTraditional ApproachPorter's Intelligence**Same Database**Multiple joinsOptimized whereHas with relationships**Different Databases**Cross-DB joins (slow/impossible)Direct queries with whereIn (fast)**Query Planning**Developer responsibilityAutomatic optimization**Connection Management**Manual configurationAutomatic detection**Fallback Strategies**NoneIntelligent degradation### Advanced Configuration Examples

[](#advanced-configuration-examples)

**Enterprise Multi-Tenant Setup**

```
// config/database.php
'connections' => [
    'tenant_app' => [
        'driver' => 'mysql',
        'host' => env('TENANT_DB_HOST'),
        'database' => env('TENANT_DB_NAME'),
    ],
    'shared_roles' => [
        'driver' => 'mysql',
        'host' => env('ROLES_DB_HOST'),
        'database' => 'shared_rbac',
    ],
],

// .env
PORTER_DB_CONNECTION=shared_roles
```

**Microservice Role Federation**

```
// Service A: User Management
class User extends Model {
    protected $connection = 'service_a_db';
    use CanBeAssignedToEntity;
}

// Service B: Project Management
class Project extends Model {
    protected $connection = 'service_b_db';
    use ReceivesRoleAssignments;
}

// Shared role assignments across services
// PORTER_DB_CONNECTION=federated_roles
```

### Troubleshooting Cross-Database Issues

[](#troubleshooting-cross-database-issues)

**Connection Validation**

```
// Check Porter's database connection
php artisan tinker
> (new \Hdaklue\Porter\Models\Roster)->getConnectionName();

// Verify model connections
> (new App\Models\User)->getConnectionName();
> (new App\Models\Project)->getConnectionName();
```

**Performance Monitoring**

```
// Enable query logging to monitor cross-database performance
DB::enableQueryLog();

// Execute cross-database role queries
$projects = Project::withAssignmentsTo($user)->get();

// Review executed queries
dd(DB::getQueryLog());
```

**Migration Considerations**

```
// When migrating existing role systems to multi-database
// 1. Install Porter on dedicated connection
php artisan porter:install

// 2. Configure separate connection
PORTER_DB_CONNECTION=roles_db

// 3. Migrate data with connection awareness
php artisan migrate --database=roles_db
```

### Cross-Database Best Practices

[](#cross-database-best-practices)

**Performance Optimization**

- Use indexed queries on role assignments for large datasets
- Consider connection pooling for high-traffic applications
- Monitor cross-database query performance in production

**Security Considerations**

- Ensure proper database-level access controls
- Use encrypted connections for cross-database communication
- Implement consistent backup strategies across databases

**Scalability Patterns**

- Design for eventual consistency in distributed role updates
- Consider read replicas for role query performance
- Plan for database sharding strategies if needed

---

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

[](#configuration)

The `config/porter.php` file contains all package settings with configurable options:

```
return [
    // Cross-Database Configuration - Enterprise-Ready
    'database_connection' => env('PORTER_DB_CONNECTION'), // null = default connection

    // ID Strategy - Works with your existing models
    'id_strategy' => env('PORTER_ID_STRATEGY', 'ulid'),

    // Security settings with enterprise encryption
    'security' => [
        'assignment_strategy' => env('PORTER_ASSIGNMENT_STRATEGY', 'replace'), // 'replace' or 'add'
        'key_storage' => env('PORTER_KEY_STORAGE', 'encrypted'),  // 'encrypted', 'hashed' or 'plain'
        'auto_generate_keys' => env('PORTER_AUTO_KEYS', true),
        'hash_rounds' => env('PORTER_HASH_ROUNDS', 12), // bcrypt rounds for hashed storage
    ],

    // High-Performance Caching
    'cache' => [
        'enabled' => env('PORTER_CACHE_ENABLED', true),
        'connection' => env('PORTER_CACHE_CONNECTION', 'default'),
        'key_prefix' => env('PORTER_CACHE_PREFIX', 'porter'),
        'ttl' => env('PORTER_CACHE_TTL', 3600), // 1 hour
        'use_tags' => env('PORTER_CACHE_USE_TAGS', true),
    ],

    // Database Performance Tuning
    'database' => [
        'transaction_attempts' => env('PORTER_DB_TRANSACTION_ATTEMPTS', 3),
        'lock_timeout' => env('PORTER_DB_LOCK_TIMEOUT', 10),
    ],
];
```

### Cross-Database Security Configuration

[](#cross-database-security-configuration)

```
// .env file - Enterprise Security Settings
PORTER_DB_CONNECTION=secure_rbac    # Dedicated secure connection for role data

PORTER_ASSIGNMENT_STRATEGY=replace  # Default: Replaces existing roles
PORTER_ASSIGNMENT_STRATEGY=add      # Adds new roles alongside existing ones

PORTER_KEY_STORAGE=encrypted  # Enterprise (default) - Laravel encrypted keys
PORTER_KEY_STORAGE=hashed     # Secure - Bcrypt hashed role keys
PORTER_KEY_STORAGE=plain      # Debug mode only - Plain text role keys

PORTER_HASH_ROUNDS=12         # Bcrypt rounds for hashed storage (production)
PORTER_AUTO_KEYS=true         # Auto-generate keys from class names

# Cross-Database Performance & Security
PORTER_CACHE_CONNECTION=redis       # Dedicated cache connection
PORTER_DB_TRANSACTION_ATTEMPTS=3    # Transaction retry attempts
PORTER_DB_LOCK_TIMEOUT=10          # Database lock timeout (seconds)
```

**Cross-Database Security Benefits:**

- **Data Isolation**: Role assignments isolated from application data
- **Access Control**: Separate database credentials for role management
- **Audit Trails**: Centralized role assignment logging
- **Compliance**: Meet data sovereignty requirements
- **Backup Strategy**: Independent backup schedules for role data

---

Laravel Integration
-------------------

[](#laravel-integration)

Porter integrates seamlessly with Laravel's authorization system - Gates, Policies, Blade directives, and middleware all work naturally with Porter's entity-specific roles.

```
// In your Policy
public function update(User $user, Project $project)
{
    return $user->hasRoleOn($project, 'admin');
}

// ⭐ POWERFUL: Hierarchy-based permission checking
public function manageTeam(User $user, Project $project)
{
    // Check if user has at least Manager level role on the project
    return $user->isAtLeastOn(new Manager(), $project);
}

// This works for complex hierarchies - if user is Admin (level 10)
// and you check isAtLeastOn(new Editor(), $project) -> true!
// Perfect for policies that need "at least this level" logic

// In your Controller
$this->authorize('update', $project);

// In your Blade templates
@can('update', $project)
    Edit Project
@endcan

// Custom Blade Directives
@hasAssignmentOn($user, $project, new Admin())
    Admin Actions
@endhasAssignmentOn

@isAssignedTo($user, $project)
    User has a role on this project
@endisAssignedTo

// Route Middleware for Role Protection
Route::middleware('porter.role:admin')->group(function () {
    Route::get('/admin/dashboard', [AdminController::class, 'dashboard']);
});

// Entity-specific role middleware
Route::middleware('porter.role_on:admin,Project,{project}')->group(function () {
    Route::get('/projects/{project}/admin', [ProjectController::class, 'admin']);
});
```

### Custom Blade Directives

[](#custom-blade-directives)

Porter provides Blade directives that correspond directly to the trait methods:

```
{{-- Check if user has specific assignment --}}
@hasAssignmentOn($user, $project, new Admin())

        Admin Controls
        Manage Project

@endhasAssignmentOn

{{-- Check if user has any assignment on entity --}}
@isAssignedTo($user, $organization)

        Organization Member

@endisAssignedTo
```

### Route Middleware

[](#route-middleware)

Porter includes two middleware for protecting routes:

```
// Protect routes requiring specific roles (any entity)
Route::middleware('porter.role:admin')->group(function () {
    Route::get('/admin/dashboard', [AdminController::class, 'dashboard']);
    Route::get('/admin/users', [AdminController::class, 'users']);
});

// Protect routes requiring roles on specific entities
Route::middleware('porter.role_on:admin,Project,{project}')->group(function () {
    Route::get('/projects/{project}/settings', [ProjectController::class, 'settings']);
    Route::post('/projects/{project}/delete', [ProjectController::class, 'destroy']);
});

// Middleware parameters:
// porter.role_on:{role},{EntityClass},{routeParameter}
Route::middleware('porter.role_on:manager,Organization,{organization}')->group(function () {
    Route::resource('organizations.teams', TeamController::class);
});

// "Any Role" functionality - user must have ANY role on the entity
Route::middleware('porter.role_on:project,*')->group(function () {
    Route::get('/projects/{project}/dashboard', [ProjectController::class, 'dashboard']);
    Route::get('/projects/{project}/activity', [ProjectController::class, 'activity']);
});

// Alternative syntax using 'anyrole' keyword
Route::middleware('porter.role:anyrole')->group(function () {
    Route::get('/projects/{project}/members', [ProjectController::class, 'members']);
});
```

**🔗 [Complete Laravel Integration Guide →](docs/laravel-integration.md)**

Learn about Policies, Middleware, Blade directives, Form Requests, API Resources, Event Listeners, and Testing with Porter.

---

Migration Strategy
------------------

[](#migration-strategy)

> *"Porter adapts to YOUR existing models AND database architecture - no changes required!"*

### Zero-Downtime Cross-Database Migration

[](#zero-downtime-cross-database-migration)

Porter works **parallel** to your existing role system, with intelligent cross-database support:

```
// Phase 1: Install Porter with cross-database configuration
composer require hdaklue/porter

// Configure dedicated role database (optional but recommended)
// .env
PORTER_DB_CONNECTION=dedicated_roles

php artisan porter:install
php artisan migrate --database=dedicated_roles  // Isolated role tables

// Phase 2: Add traits to existing models (no database changes)
class User extends Authenticatable
{
    use HasUlids;  // Modern ID strategy
    protected $connection = 'main_app_db';  // Existing connection

    // All existing code works unchanged!
}

class Project extends Model
{
    protected $connection = 'projects_db';  // Different connection
    // Porter automatically handles cross-database queries
}

// Phase 3: Gradually migrate role checks (systems run parallel)
// Old system keeps working:
if ($user->hasRole('admin')) { /* existing code */ }

// New Porter system with cross-database intelligence:
if ($user->hasRoleOn($project, 'admin')) { /* Porter auto-optimizes */ }

// Phase 4: Switch over when ready - no rush, zero downtime!
```

### Enterprise Migration Patterns

[](#enterprise-migration-patterns)

**Multi-Database Environment Migration**

```
// Before: Traditional single-database roles
// Database: main_app (users, roles, role_user tables)

// After: Cross-database Porter architecture
// Database 1: main_app (users, projects) - existing data intact
// Database 2: roles_central (roster table) - new Porter data
// Result: Zero disruption, enhanced performance

// Migration command for existing data
php artisan porter:migrate-from-existing --source-connection=main_app
```

**Microservice Migration Strategy**

```
// Service A: Keep existing models, add Porter traits
class User extends Model {
    protected $connection = 'service_a_users';
    use CanBeAssignedToEntity; // Porter trait
}

// Service B: Different database, Porter handles cross-service roles
class Project extends Model {
    protected $connection = 'service_b_projects';
    use ReceivesRoleAssignments; // Porter trait
}

// Shared Role Service: Centralized role management
// PORTER_DB_CONNECTION=shared_roles_service
```

### Flexible Architecture Support

[](#flexible-architecture-support)

```
// config/porter.php - Adapts to any architecture
'database_connection' => env('PORTER_DB_CONNECTION'), // null = same DB
'id_strategy' => 'ulid',     // Modern ULID IDs
'id_strategy' => 'uuid',     // UUID support
'id_strategy' => 'integer',  // Legacy auto-increment

// Enterprise deployment options:
// Option 1: Same database (traditional)
PORTER_DB_CONNECTION=null

// Option 2: Dedicated role database (recommended)
PORTER_DB_CONNECTION=role_management_db

// Option 3: Microservice role federation
PORTER_DB_CONNECTION=shared_role_service
```

---

Performance
-----------

[](#performance)

### Intelligent Cross-Database Architecture

[](#intelligent-cross-database-architecture)

Porter uses **exactly ONE database table** (`roster`) with sophisticated cross-database optimization:

```
-- The ENTIRE role system in one optimized table:
CREATE TABLE roster (
    id bigint PRIMARY KEY,
    assignable_type varchar(255),  -- 'App\Models\User'
    assignable_id varchar(255),    -- ULID: '01HBQM5F8G9YZ2XJKPQR4VWXYZ'
    roleable_type varchar(255),    -- 'App\Models\Project'
    roleable_id varchar(255),      -- ULID: '01HBQM6G9HAZB3YLKQRS5WXYZA'
    role_key varchar(255),         -- Encrypted: 'eyJpdiI6IlBzc...'
    created_at timestamp,
    updated_at timestamp,

    -- Optimized indexes for cross-database performance
    UNIQUE KEY porter_unique (assignable_type, assignable_id, roleable_type, roleable_id, role_key),
    INDEX porter_assignable (assignable_type, assignable_id),
    INDEX porter_roleable (roleable_type, roleable_id),
    INDEX porter_role_key (role_key)
);
```

### Cross-Database Performance Benefits

[](#cross-database-performance-benefits-1)

**Query Strategy Intelligence:**

- 🚀 **Same Database**: Optimized JOINs with relationship caching
- ⚡ **Different Databases**: Direct queries with whereIn optimization
- 🧠 **Automatic Detection**: Zero configuration required
- 📊 **Query Planning**: Intelligent fallback strategies

**Performance Metrics:**

- 🏃 **50-200x faster** than traditional RBAC systems
- 💾 **Minimal memory footprint** - 8 core classes, &lt;1MB
- ⚡ **Zero cross-database JOIN overhead** - smart query strategies
- 🔄 **High-performance caching** - Redis-optimized with tagging
- 📈 **Linear scaling** - performance doesn't degrade with database separation
- 🧪 **Performance validated** - 12 scalability tests confirm 1000+ assignments handle efficiently

### Enterprise Performance Patterns

[](#enterprise-performance-patterns)

**Multi-Database Query Optimization**

```
// Porter automatically chooses the optimal strategy

// Same database: Fast JOINs with relationships
$projects = Project::withRole(new Admin())->get();
// Executes: SELECT * FROM projects INNER JOIN roster...

// Different databases: Direct queries with IN clauses
$projects = Project::withAssignmentsTo($user)->get();
// Executes: 1) SELECT roleable_id FROM roster WHERE...
//          2) SELECT * FROM projects WHERE id IN (...)
```

**Caching Strategy for Cross-Database**

```
// Intelligent cache keys account for database separation
'cache_key' => 'porter:user:123:project_db:456:admin'
//             'porter:{assignable}:{db}:{roleable}:{role}'

// Cross-database cache invalidation
'cache_tags' => ['porter_user_123', 'porter_project_456', 'porter_admin']
```

**Performance Benefits by Architecture:**

ArchitectureQuery CountJoin StrategyCache StrategyPerformance Gain**Single Database**1 optimized queryEloquent relationshipsStandard caching50x faster**Cross-Database**2 direct querieswhereIn optimizationCross-DB cache tags100x faster**Microservices**Service-specificAPI-friendly queriesFederated caching200x faster**Additional Performance Features:**

- 🎯 **Connection pooling** support for high-traffic applications
- 📊 **Query result caching** with intelligent invalidation
- 🔄 **Read replica support** for role query distribution
- ⚡ **Batch operations** for bulk role assignments
- 📈 **Horizontal scaling** readiness

---

CLI Commands
------------

[](#cli-commands)

Porter provides several Artisan commands for role management:

### Installation Commands

[](#installation-commands)

```
# Basic installation (config, migrations, Porter directory)
php artisan porter:install

# Full installation with default roles
php artisan porter:install --roles

# Force overwrite existing files
php artisan porter:install --roles --force
```

### Role Management Commands

[](#role-management-commands)

```
# Interactive role creation with guided setup
php artisan porter:create

# Create specific role with description
php artisan porter:create ProjectManager --description="Manages development projects"

# List all available roles
php artisan porter:list

# Validate Porter setup and configuration
php artisan porter:doctor
```

#### `porter:create` Command Deep Dive

[](#portercreate-command-deep-dive)

The `porter:create` command is Porter's intelligent role creation system that handles automatic hierarchy management and level calculation.

**Interactive Mode (Recommended):**

```
php artisan porter:create
```

**Command Line Mode:**

```
php artisan porter:create RoleName --description="Role description"
```

**🎯 Smart Hierarchy Management:**

When creating roles, Porter offers intelligent positioning options:

1. **`lowest`** - Creates role at level 1, pushes all existing roles up
2. **`highest`** - Creates role above all existing roles
3. **`lower`** - Creates role at same level as selected role, pushes target role up
4. **`higher`** - Creates role one level above selected role

**Example Interactive Flow:**

```
$ php artisan porter:create
🎭 Creating a new Porter role...

 What is the role name? (e.g., Admin, Manager, Editor):
 > ProjectManager

 What is the role description? [User with ProjectManager role privileges]:
 > Manages development projects and team coordination

 How would you like to position this role?

  lowest - Create as the lowest level role (level 1)
  highest - Create as the highest level role
  lower - Create at same level as existing role (pushes that role up)
  higher - Create one level higher than existing role

 Select creation mode:
 > higher

 Which role do you want to reference?
 > Editor

 Updating levels of existing roles...
    - Updated Admin from level 8 to 9
    - Updated Manager from level 6 to 7

 ✅ Role 'ProjectManager' created successfully!
 📁 Location: app/Porter/ProjectManager.php
 🔢 Level: 7
 📝 Description: Manages development projects and team coordination
 🔑 Key: project_manager

 Don't forget to:
 1. Add the role to your config/porter.php roles array
 2. Run 'php artisan porter:doctor' to validate your setup
```

**Generated Role Class:**

```
