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

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 denied

How 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>
@endteamPermission

Each 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 file
namespace 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 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.