Granting permissions
Grant through Permissions::for($holder). givePermissionTo is additive — it never strips grants another caller registered — so independent services can each register their own permissions safely. syncPermissions is authoritative: the given set becomes the complete set. The same verbs on the model are shorthand for the same calls:
use RoundlyConsulting\Permissions\Facades\Permissions;
Permissions::for($role)->givePermissionTo('auth.users.view', 'auth.users.edit'); // additive
Permissions::for($role)->syncPermissions(['auth.users.view']); // exact set
Permissions::for($role)->revokePermissionTo('auth.users.view');
// The same, on the model — the trait delegates to the same manager:
$role->givePermissionTo('auth.users.view', 'auth.users.edit');
$role->syncPermissions(['auth.users.view']);
$role->revokePermissionTo('auth.users.view');Accepted arguments
Every grant method accepts permission names, backed enums, Permission models, arrays and nested iterables — mixed freely and de-duplicated. givePermissionTo and revokePermissionTo are variadic; syncPermissions takes a single iterable:
$role->givePermissionTo('auth.users.view'); // one name
$role->givePermissionTo('auth.users.view', 'auth.users.edit'); // variadic
$role->givePermissionTo(['auth.users.view', 'auth.users.edit']); // an array
$role->givePermissionTo([$permission, PermissionName::EditUsers]); // models + backed enums
$role->revokePermissionTo('auth.users.view', 'auth.users.edit'); // variadic too
$role->syncPermissions([$permission, 'auth.users.edit']); // one iterableAn unknown name throws PermissionDoesNotExist; an unsaved Permission model throws PermissionException. Register permissions with Permissions::permission() or Permissions::syncFrom() before you grant them.
Direct grants on a model
The same verbs work on any HasRoles model and manage its direct grants, independent of its roles. syncPermissions on a user replaces only the direct set — permissions inherited through roles are untouched:
// Direct grants on the user itself — independent of any role
Permissions::for($user)->givePermissionTo('reports.export');
Permissions::for($user)->syncPermissions(['reports.export', 'reports.schedule']); // replaces the direct set only
Permissions::for($user)->revokePermissionTo('reports.schedule');
$user->givePermissionTo('reports.export'); // the trait shorthandWhy additive matters
Modules can grant into the same role without coordinating with each other:
// Billing module
$role->givePermissionTo('billing.invoices.view');
// Reports module — registers independently, never strips billing's grant
$role->givePermissionTo('reports.export');
$role->getPermissionNames(); // billing.invoices.view + reports.exportGrant modes
The two behaviours are modelled by the GrantMode enum:
use RoundlyConsulting\Permissions\Enums\GrantMode;
GrantMode::Additive->detaches(); // false — givePermissionTo()
GrantMode::Authoritative->detaches(); // true — syncPermissions()Every permission write runs inside a database transaction, so a concurrent Gate check never observes a half-applied sync, and every write invalidates the permission catalog cache once it commits.
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.