Skip to main content

HasRole Trait

The HasRole trait turns any Eloquent model into a personable actor that can hold memberships, evaluate permissions, and integrate with Gate, middleware, and Blade.

When to Use

Use HasRole when you need:

  • Permission checks on users (or any personable model)
  • Assigning / syncing / removing roles in system or memberable scope
  • Super-role and owner shortcuts
  • Collection-scoped memberships
  • Request-scoped memoization and shared permission cache

Example scenarios:

  • Staff accounts with system roles
  • Tenant members with scoped roles
  • API clients that must pass Gate / middleware checks

Namespace

JobMetric\Rolix\Traits\HasRole

Basic Usage

use Illuminate\Foundation\Auth\User as Authenticatable;
use JobMetric\Rolix\Traits\HasRole;

class User extends Authenticatable
{
use HasRole;
}

Permission Evaluation Order

When hasPermission() / getPermissions() run:

  1. Active super membership → allow: ['*']
  2. Owner membership for the given memberable (+ optional collection) → allow: ['*'] in that context only
  3. Load non-expired memberships for the scope
  4. Resolve roles (and valid ancestors whose rules pass)
  5. Merge membership + role allow / deny
  6. Deny wins over allow; supports exact keys, *, and prefix.*

Methods

hasPermission()

Check whether the person has a permission.

public function hasPermission(
string $permission,
?Model $context = null,
?string $collection = null
): bool
ParameterTypeDescription
$permissionstringPermission key (e.g. hero.view)
$contextModel|nullMemberable model, or null for system scope
$collectionstring|nullOptional membership collection filter

Returns: bool

$user->hasPermission('hero.view');
$user->hasPermission('hero.view', $tenant);
$user->hasPermission('hero.view', $tenant, 'ops');

Internally uses getPermissions() then deny/allow matching (including wildcards).


getPermissions()

Return the effective allow/deny lists for a scope.

public function getPermissions(
?Model $context = null,
?string $collection = null
): array

Returns: array{allow: array<int, string>, deny: array<int, string>}

$perms = $user->getPermissions($tenant);
// ['allow' => ['workspace.*'], 'deny' => ['billing.refund']]

Results are memoized on the model for the current request and, when enabled, stored in Laravel Cache via PermissionCache.


hasRole()

Whether the person has the given role in scope (by model, id, or name).

public function hasRole(
Role|int|string $role,
?Model $context = null,
?string $collection = null
): bool
$user->hasRole($role);
$user->hasRole(12, $tenant);
$user->hasRole('Editor', $tenant, 'ops');

assignRole()

Create a membership through the Membership service.

public function assignRole(
Role|int $role,
?Model $context = null,
array $attributes = []
): Membership
ParameterDescription
$roleRole model or id
$contextMemberable model, or null for system
$attributesExtra membership fields (collection, is_owner, allow, deny, expired_at, …)

Returns: Membership model

$user->assignRole($role);
$user->assignRole($role, $tenant, [
'is_owner' => true,
'collection' => 'ops',
'allow' => ['extra.perm'],
]);

Invalidates permission cache for this personable after store.


removeRole()

Soft-delete memberships matching the role in scope.

public function removeRole(
Role|int $role,
?Model $context = null,
?string $collection = null
): int

Returns: number of destroyed memberships

$count = $user->removeRole($role, $tenant);

syncRoles()

Replace all memberships in scope with the given role ids.

public function syncRoles(
array $roles,
?Model $context = null,
?string $collection = null
): void
$user->syncRoles([$roleA, $roleB->id], $tenant, 'ops');

Removes memberships not in $roles, assigns missing ones, then forgets cache.


forgetRolixCache()

Clear request-scoped memoization and the loaded memberships relation.

public function forgetRolixCache(): void

For shared cache invalidation use PermissionCache::forget($user) (also clears memo).


memberships()

public function memberships(): MorphMany

Morph-many relation as personable.

Scenarios

System admin

$user->assignRole($superRole); // is_super on role
$user->hasPermission('anything'); // true

Tenant owner

$user->assignRole($role, $tenant, ['is_owner' => true]);
$user->hasPermission('anything', $tenant); // true
$user->hasPermission('anything'); // false (different scope)

Wildcards

// role allow: ['hero.*'], deny: ['hero.secret']
$user->hasPermission('hero.view'); // true
$user->hasPermission('hero.secret'); // false

Best Practices

  • Prefer hasPermission() over raw membership queries
  • Pass memberable context for multi-tenant checks
  • Keep permission keys dotted (module.action) so trees and wildcards work
  • After external membership changes on an already-loaded model, call PermissionCache::forget($user) or reload the model