minaremonshaker's avatar

Spatie Permissions + Laravel Policies: Best Practice for Permission/Role Controllers?

I'm building an admin panel using Spatie Laravel Permission and I'm confused about the "right way" to handle authorization in Permission/Role controllers.

Option A (Direct Spatie check):

if (!auth()->user()->hasPermissionTo('view_any_permission')) {
    abort(403);
}

Option B (Laravel Policy):

Gate::authorize('viewAny', Permission::class);
// Policy just wraps: return $user->hasPermissionTo('view_any_permission');

I've seen both approaches but get mixed advice:

  • Spatie docs seem to favor direct checks - Permission/Role models are "internal admin tools"

  • Laravel docs push policies - Consistent authorization pattern across app

  • My current setup: Already created PermissionPolicy ✅ registered ✅ working

Questions:

  • Is PermissionPolicy truly "over-engineering" for admin screens?

  • When Spatie already integrates with Gates ($user->can()), what's the value of wrapping it in a policy?

  • Community standard: Direct Spatie checks vs Policies for Permission/Role management?

Context:

 -  Laravel 12, Spatie v6

 -  Using policies successfully for business models (PostPolicy, UserPolicy, TicketPolicy)

 -  Permissions follow snake_case convention (view_any_permission)

Current working code:

Gate::authorize('viewAny', Permission::class);

// Policy
public function viewAny(User $user): bool
{
    return $user->hasPermissionTo('view_any_permission');
}

Should I keep the policy pattern for consistency, or switch Permission/Role controllers to direct Spatie checks for simplicity?

0 likes
5 replies
minaremonshaker's avatar

hi thank you for the answer ,what i want is to make access control even for the permissions model and roles model where admin user can do every thing and manger can do spacific things in the admin panel

LaryAI's avatar
Level 58

Great question—this is a common dilemma in Laravel apps using Spatie Laravel Permission.

1. Is PermissionPolicy "over-engineering" for admin screens?

Not really. If you're already using policies for other models (i.e., PostPolicy, UserPolicy), having a PermissionPolicy (and possibly RolePolicy) is not overkill. It keeps your authorization logic consistent, centralized, and leverages the Laravel tools your team is already using. Policies are lightweight, easy to test, and clearly document "who can do what" in one place.

2. If Spatie integrates with Gates ($user->can()), what's the value of wrapping it in a policy?

  • Consistency: Your controllers always use $this->authorize(...) or Gate::authorize(...) for all protected actions, which is less confusing for new devs or when you revisit your codebase.
  • Swapping logic: In the future, if your permission checks become more complex (e.g., super-admin bypass, tenant scoping, feature flags), you only edit the policy, not dozens of controllers.
  • Laravel Features: Policies integrate with resource controllers, Blade directives (@can, @cannot), and authorization responses, making front-end and API consistency easier.

3. Community standard: Direct Spatie checks vs. Policies?

  • For business models: Almost always policies.
  • For admin-only "internal" models: Some use direct checks, especially in ultra-simple apps. However, most medium/large Laravel apps use policies for all authorization, including Permission/Role, for the above reasons.

In large teams/projects, consistency > minor performance/convenience.


Recommended Practice — Stick With Policies for Permissions and Roles

Since you already have PermissionPolicy and it's working:
Keep using it for your admin controllers, just like your other models. Your current code is ideal:

Gate::authorize('viewAny', Permission::class);

// In PermissionPolicy
public function viewAny(User $user): bool
{
    return $user->hasPermissionTo('view_any_permission');
}

Now if you ever need to add exceptions or extra logic, you change a single place.


Bonus: Clean Controller Example

public function index()
{
    $this->authorize('viewAny', Permission::class);

    $permissions = Permission::all();

    return view('permissions.index', compact('permissions'));
}

Summary Table

Approach Pros Cons
Direct Spatie Check Simple, fewer files Inconsistent, scattered logic, not Laravel-y
Policy Consistent, centralizes logic; easier long-term maintenance; integrates with Laravel UI features Slightly more boilerplate (already done)

Final Answer

Stick with policies for Permission and Role controllers if you already use policies elsewhere. This aligns with community standards, maintains code consistency, and future-proofs your authorization logic.

Direct Spatie checks are fine for minimal apps, but policies are best practice for anything larger or maintained by a team.

Snapey's avatar

If you use gate you can add additional conditions like allowing admin to do anything without explicitly granting permission.

minaremonshaker's avatar

hi thank you for the answer , i will do that of course but i want to ask you about lary`s answer what do you think about it ?

Snapey's avatar
Snapey
Best Answer
Level 122

I think about it this way. If you just want to check ABILITY then use a Gate. ie, can this user edit Users, yes or no? Use policies if you need control at the model level, ie can this user edit Users from team X. Your policy can consume permissions directly or via gates.

Gate = general ability.

Policy = ability to do something with specific thing.

Please or to participate in this conversation.