The Permissions facade
Everything goes through the Permissions facade (auto-aliased as Permissions). Its root is RoundlyConsulting\Permissions\PermissionsManager, a singleton you can also inject. Every name-taking method accepts a string or a BackedEnum:
use RoundlyConsulting\Permissions\Facades\Permissions;
// Catalog
Permissions::role('editor'); // find or create — Role
Permissions::permission('posts.edit'); // find or create — Permission
Permissions::findRole('editor'); // ?Role
Permissions::findPermission('posts.edit'); // ?Permission — the full row
Permissions::roles(); // Collection<Role>, ordered by name
Permissions::permissions(); // the cached catalog: id + name
Permissions::exists('posts.edit'); // bool, from the cache
Permissions::syncFrom(PostPermission::class); // SyncResult
Permissions::syncRolesFrom(RoleName::class); // SyncResult
// Grants for one holder — every verb returns the holder
Permissions::for($user)->assignRole('editor');
Permissions::for($user)->removeRole('editor');
Permissions::for($user)->syncRoles(['editor']);
Permissions::for($user)->givePermissionTo('posts.edit');
Permissions::for($user)->revokePermissionTo('posts.edit');
Permissions::for($user)->syncPermissions([PostPermission::Edit]);
Permissions::for($user)->forgetAllAuthorization();
// Scopes, windows and owned roles — need permissions.assignments.enabled
Permissions::for($user)->in($project)->until($friday)->assignRole('editor');
Permissions::for($user)->in($project)->hasPermissionTo('posts.edit'); // bool
Permissions::scope($project); // PermissionScope — the Gate argument
Permissions::owner($tenant)->role('reviewer'); // OwnedRoles — a tenant's own roles
Permissions::resolveScopeUsing($resolver); // ambient scope for scope-less Gate checks
Permissions::resolveRoleOwnerUsing($resolver); // let a scope use another owner's roles
Permissions::forgetScope($project); // int — rows and owned roles deleted
Permissions::pruneExpired(); // int — rows whose window has ended
// Housekeeping and configuration
Permissions::cache()->forget();
Permissions::cache()->flushMemo();
Permissions::pruneOrphans(); // int — rows deleted
Permissions::roleModel(); // class-string<Role>
Permissions::permissionModel(); // class-string<Permission>
// Tests
$fake = Permissions::fake(); // PermissionsFakeMethods
| Method | Returns | Purpose |
|---|---|---|
role($name) | Role | Find or create a role, as your configured model. |
permission($name) | Permission | Find or create a permission, as your configured model. |
findRole($name) | ?Role | Look a role up by name; null when missing. A live query. |
findPermission($name) | ?Permission | Look a permission up by name — the full row, description included. |
roles() | Collection<Role> | Every role, ordered by name. A live query. |
permissions() | Collection<Permission> | The cached permission catalog — id and name only. |
exists($name) | bool | Whether a permission is registered, answered from the cache. |
syncFrom(Enum::class) | SyncResult | Register one permission per case of a backed enum. Additive. |
syncRolesFrom(Enum::class) | SyncResult | Register one role per case of a backed enum. Additive. |
for($holder) | HolderGrants | Role and permission writes and reads for one holder; in(), from(), until(), between() and at() scope and time-box it. |
owner($owner) | OwnedRoles | The roles one owner — a tenant, a team — defines for itself. |
scope($scopes) | PermissionScope | The Gate argument for a scoped check; several scopes mean any of them. |
resolveScopeUsing(?Closure) | void | Give scope-less Gate checks an ambient scope; null removes it. |
resolveRoleOwnerUsing(?Closure) | void | Let a scope also use another owner’s roles; null removes it. |
forgetScope($scope) | int | Delete every assignment in a scope and every role it owns. |
pruneExpired(?$endedBefore) | int | Delete assignment rows whose window ended by then (default now). |
cache() | PermissionCache | forget() drops the cached catalog; flushMemo() drops only this process’s memo. |
pruneOrphans() | int | Delete grant rows whose holder no longer exists; returns the count. |
roleModel() / permissionModel() | class-string | Your configured model classes. |
fake() | PermissionsFake | Swap in the recording fake — facade only. |
Writes for one holder: for()
Permissions::for($holder) returns a HolderGrants handle with the same write verbs as the traits. Every verb returns the holder:
use RoundlyConsulting\Permissions\Facades\Permissions;
Permissions::for($user)->assignRole('editor');
Permissions::for($user)->syncPermissions([PostPermission::Edit]);
$role = Permissions::role('editor');
Permissions::for($role)->givePermissionTo('posts.edit'); // a Role holds permissions
// The trait methods are sugar over the same handle:
$user->assignRole('editor'); // === Permissions::for($user)->assignRole('editor')| Permissions::for($holder)->… | Does | Holder must |
|---|---|---|
assignRole(...$roles) | Additively assign roles. | use HasRoles |
removeRole(...$roles) | Detach roles. | use HasRoles |
syncRoles($roles) | Make the given roles the exact set. | use HasRoles |
givePermissionTo(...$permissions) | Additively grant direct permissions. | use HasPermissions — a Role or any HasRoles model |
revokePermissionTo(...$permissions) | Detach direct permissions. | use HasPermissions |
syncPermissions($permissions) | Make the given direct permissions the exact set. | use HasPermissions |
forgetAllAuthorization() | Detach every role and direct permission — with assignments on, also every scoped and time-boxed row in every scope. | use HasRoles |
in($scopes) | Scope the handle: a model, a list (checks: any of them; writes: exactly one) or null for unscoped. Returns a new handle. | use HasRoles; assignments on |
from() / until() / between() | The window for writes — start inclusive, end exclusive, stored in UTC to the second. Returns a new handle. | use HasRoles; assignments on |
at($moment) | Answer reads as of that moment; writes ignore it. | use HasRoles; assignments on |
hasRole() / hasAnyRole() / hasPermissionTo() | Check in the handle’s scopes at its check time — directly or through a role. | use HasRoles |
roleNames() / permissionNames() / assignments() | What the holder has: names, or every grant as an Assignment. | use HasRoles |
The scope is a boundary. Role writes refuse a model without HasRoles — including a Role, which holds permissions, never roles. Permission writes refuse a model without HasPermissions, and both refuse a holder that has no key yet. Each refusal is a PermissionException, thrown before anything is written; unknown names throw RoleDoesNotExist or PermissionDoesNotExist.
Where each part is documented
- role(), permission(), findRole(), findPermission(), roles() — Roles & permissions.
- syncFrom(), syncRolesFrom() — Backed enums.
- for()->… role verbs — Assigning roles; permission verbs — Granting permissions.
- permissions(), exists(), cache() — Caching.
- pruneOrphans() — Deleting holders.
- for()->in(), from(), until(), between(), at(), scope(), resolveScopeUsing(), forgetScope(), pruneExpired() — Scoped & time-boxed grants.
- owner(), resolveRoleOwnerUsing() — Tenant-defined roles.
- fake() — Testing.
Show your open-source love
This package is free and MIT-licensed. If it saves you time, a one-off donation or a Patreon membership keeps it maintained, tested and documented.
More ways to support, including cryptoBy donating, you agree to our donation terms.
Want this built into your product?
We integrate our packages into custom Laravel and AI builds. Tell us what you're working on and we'll reply within 48 hours.