NewWe open-sourced 50+ Laravel packages
Custom AI apps, agents and automation — Roundly ConsultingRoundly
All packages
Permissions for Laravel

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 iterable

An 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 shorthand

Why 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.export

Grant 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 crypto

By 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.