Gate, Blade & policies
With gate.register on (the default), authorize the idiomatic Laravel way. The ability is <prefix>.<permission> and the team is the gate argument:
if ($user->can('teams.manage-billing', $team)) {
// ...
}
$user->can('teams.posts.publish', $team); // a permission ability
$user->can('teams.owner', $team); // the owner ability
Gate::authorize('teams.team.update', $team); // throws a 403 when deniedHow the gate resolves
- The package registers a Gate::before hook, not named abilities. It only answers abilities under the prefix (teams. by default) whose first argument is a Team.
- teams.owner resolves to $team->isOwnedBy($user); any other teams.<permission> resolves to memberHasPermission().
- A miss returns null, not false, so your own gates and policies still decide — the hook never blocks unrelated abilities.
- Rename with gate.prefix and gate.owner_ability, or set TEAMS_REGISTER_GATE=false to register neither the hook nor the Blade directives.
Blade directives
@teamPermission($team, 'manage-billing')
<a href="/billing">Billing</a>
@endteamPermission
@teamRole($team, 'admin')
<button>Manage team</button>
@endteamRole
@teamOwner($team)
<button>Delete team</button>
@endteamOwner
{{-- Check a specific member instead of the signed-in user --}}
@teamPermission($team, 'posts.publish', $otherUser)
<span>Can publish</span>
@endteamPermissionEach directive checks the signed-in user by default; pass a member as the last argument to check someone else. @teamRole and @teamPermission both fail for an expired membership.
Team policies
teams:policy scaffolds a policy extending AbstractTeamPolicy, whose before() grants every ability on a team to its owner and otherwise defers to your methods. Class-level abilities — $user->can('create', Team::class), viewAny — carry no team, so before() lets them through to your policy method:
php artisan teams:policy TeamPolicy # writes app/Policies/TeamPolicy.php
php artisan teams:policy TeamPolicy --force # overwrite an existing filenamespace App\Policies;
use Illuminate\Database\Eloquent\Model;
use RoundlyConsulting\Teams\Models\Team;
use RoundlyConsulting\Teams\Policies\AbstractTeamPolicy;
class TeamPolicy extends AbstractTeamPolicy
{
public function view(Model $user, Team $team): bool
{
return $this->allows($user, $team, 'team.view');
}
public function update(Model $user, Team $team): bool
{
return $this->allows($user, $team, 'team.update');
}
public function delete(Model $user, Team $team): bool
{
return $this->allows($user, $team, 'team.delete');
}
}Register it the usual way (policy auto-discovery or Gate::policy()). For hand-written policies, the HasTeamPolicies trait supplies the same owns() and allows() helpers:
use Illuminate\Database\Eloquent\Model;
use RoundlyConsulting\Teams\Concerns\HasTeamPolicies;
use RoundlyConsulting\Teams\Models\Team;
class PostPolicy
{
use HasTeamPolicies;
public function update(Model $user, Team $team): bool
{
return $this->owns($user, $team) || $this->allows($user, $team, 'posts.update');
}
}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.