This skill should be used when the user asks to "add permissions to a policy", "create a policy with roles", "set up role permissions", "configure rolePermissions", "use BasePolicy", or when working...
This skill provides guidance for implementing role-based permissions through Laravel policies using the philsquare/permissions package.
Check if the project uses this package by looking for:
philsquare/permissions in composer.jsonPhilsquare\Permissions\BasePolicyrolePermissions() method in existing policiesAll policies requiring role-based permissions must extend BasePolicy:
use Philsquare\Permissions\BasePolicy;
class PostPolicy extends BasePolicy
{
public function rolePermissions(): array
{
return [
'admin' => $this->permissions()->all(),
];
}
// Policy methods...
}
$user->can('update', $post) or Gate::authorize('update', $post)update() methodBasePolicy::before() runs first, checking if user's roles have the permissionpost:update), the policy method executesThe before() hook converts:
PostPolicy to postupdateEta to update-etapost:update-etaPermissions are stored in kebab-case: {model}:{action}
| Policy Method | Database Permission |
|---|---|
viewAny() |
model:view-any |
forceDelete() |
model:force-delete |
updateEta() |
model:update-eta |
Define which roles can access which policy methods:
public function rolePermissions(): array
{
return [
'admin' => $this->permissions()->all(),
'manager' => $this->permissions()->crud(['approve', 'reject']),
'editor' => $this->permissions()->only(['viewAny', 'view', 'update']),
'viewer' => $this->permissions()->only(['viewAny', 'view']),
];
}
Access via $this->permissions() in any policy extending BasePolicy.
all()Returns all public methods (excluding system methods like before, allow, deny).
crud(array $additional = [])Returns: viewAny, view, create, update, delete plus any additional methods.
$this->permissions()->crud(['publish', 'archive'])
// Returns: viewAny, view, create, update, delete, publish, archive
only(array $methods)Returns only the specified methods.
$this->permissions()->only(['viewAny', 'view'])
except(array $methods)Returns all methods except specified ones.
$this->permissions()->except(['delete', 'forceDelete'])
Policy methods define business logic for when actions are allowed:
public function update(User $user, Order $order): bool
{
// Role check happens in before() hook
// This method handles additional business logic
return $order->status === 'draft';
}
Return true to allow, false to deny. The role permission check happens first in before().
After modifying policies, run:
php artisan permissions:refresh
This scans all policies extending BasePolicy and syncs role-permission mappings to the database.
Add to deployment scripts to auto-sync on deploy.
Roles are created automatically when permissions:refresh runs. Use an enum for type safety:
namespace App\Enums;
enum Roles: string
{
case Admin = 'admin';
case Manager = 'manager';
case Editor = 'editor';
}
Use in policies:
use App\Enums\Roles;
public function rolePermissions(): array
{
return [
Roles::Admin->value => $this->permissions()->all(),
];
}
For detailed helper documentation, consult:
references/permission-helpers.md - Complete helper method reference with examples