Per-team role overrides
Enable roles.per_team to let each team diverge from the global roles, so one tenant’s Editor can do more than another’s. An override for a (team, key) pair wins over the global definition; every other key falls back to the global role. With the flag off, resolution goes straight to the global provider with no extra queries:
// .env → TEAMS_PER_TEAM_ROLES=true
Teams::roles()->register('editor', 'Editor', ['posts.edit']); // global baseline
Teams::for($team)->roles()->define(
'editor',
'Senior Editor',
['posts.edit', 'posts.publish'],
'Editor with publishing rights for this workspace',
);
// or: $team->defineRole('editor', 'Senior Editor', ['posts.edit', 'posts.publish']);
$user->can('teams.posts.publish', $team); // true for THIS team only
Teams::for($team)->roles()->all(); // effective map — global roles merged with overrides
Teams::for($team)->roles()->find('editor'); // ?Role — the override when one existsroles()->define() upserts a team_role_overrides row on (team_id, key) — call it again to change the override; a soft-deleted override is restored and redefined. Permissions may be strings or Permission objects; only their keys are stored.
Resolving roles for a team
use RoundlyConsulting\Teams\Roles\TeamRoleResolver;
$resolver = app(TeamRoleResolver::class);
$resolver->resolve($team, 'editor'); // ?Role — the team's override if any, else the global role
$resolver->all($team); // array<string, Role>
$team->teamRoles()->where('key', 'editor')->first()?->toRole(); // one override as a RoleMember::role() goes through the resolver only while per_team is on — overrides are ignored when it’s off, even if rows exist. TeamRoleResolver is a container singleton you can rebind.
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.