mirror of
https://github.com/openclassify/openclassify.git
synced 2026-04-14 11:12:09 -05:00
Merge pull request #1392 from openclassify/fatihalp-patch-1
Delete .agents/skills/laravel-permission-development/SKILL.md
This commit is contained in:
commit
568368bb17
@ -1,277 +0,0 @@
|
||||
---
|
||||
name: laravel-permission-development
|
||||
description: Build and work with Spatie Laravel Permission features, including roles, permissions, middleware, policies, teams, and Blade directives.
|
||||
---
|
||||
|
||||
# Laravel Permission Development
|
||||
|
||||
## When to use this skill
|
||||
|
||||
Use this skill when working with authorization, roles, permissions, access control, middleware guards, or Blade permission directives using spatie/laravel-permission.
|
||||
|
||||
## Core Concepts
|
||||
|
||||
- **Users have Roles, Roles have Permissions, Apps check Permissions** (not Roles).
|
||||
- Direct permissions on users are an anti-pattern; assign permissions to roles instead.
|
||||
- Use `$user->can('permission-name')` for all authorization checks (supports Super Admin via Gate).
|
||||
- The `HasRoles` trait (which includes `HasPermissions`) is added to User models.
|
||||
|
||||
## Setup
|
||||
|
||||
Add the `HasRoles` trait to your User model:
|
||||
|
||||
```php
|
||||
use Spatie\Permission\Traits\HasRoles;
|
||||
|
||||
class User extends Authenticatable
|
||||
{
|
||||
use HasRoles;
|
||||
}
|
||||
```
|
||||
|
||||
## Creating Roles and Permissions
|
||||
|
||||
```php
|
||||
use Spatie\Permission\Models\Role;
|
||||
use Spatie\Permission\Models\Permission;
|
||||
|
||||
$role = Role::create(['name' => 'writer']);
|
||||
$permission = Permission::create(['name' => 'edit articles']);
|
||||
|
||||
// findOrCreate is idempotent (safe for seeders)
|
||||
$role = Role::findOrCreate('writer', 'web');
|
||||
$permission = Permission::findOrCreate('edit articles', 'web');
|
||||
```
|
||||
|
||||
## Assigning Roles and Permissions
|
||||
|
||||
```php
|
||||
// Assign roles to users
|
||||
$user->assignRole('writer');
|
||||
$user->assignRole('writer', 'admin');
|
||||
$user->assignRole(['writer', 'admin']);
|
||||
$user->syncRoles(['writer', 'admin']); // replaces all
|
||||
$user->removeRole('writer');
|
||||
|
||||
// Assign permissions to roles (preferred)
|
||||
$role->givePermissionTo('edit articles');
|
||||
$role->givePermissionTo(['edit articles', 'delete articles']);
|
||||
$role->syncPermissions(['edit articles', 'delete articles']);
|
||||
$role->revokePermissionTo('edit articles');
|
||||
|
||||
// Reverse assignment
|
||||
$permission->assignRole('writer');
|
||||
$permission->syncRoles(['writer', 'editor']);
|
||||
$permission->removeRole('writer');
|
||||
```
|
||||
|
||||
## Checking Roles and Permissions
|
||||
|
||||
```php
|
||||
// Permission checks (preferred - supports Super Admin via Gate)
|
||||
$user->can('edit articles');
|
||||
$user->canAny(['edit articles', 'delete articles']);
|
||||
|
||||
// Direct package methods (bypass Gate, no Super Admin support)
|
||||
$user->hasPermissionTo('edit articles');
|
||||
$user->hasAnyPermission(['edit articles', 'publish articles']);
|
||||
$user->hasAllPermissions(['edit articles', 'publish articles']);
|
||||
$user->hasDirectPermission('edit articles');
|
||||
|
||||
// Role checks
|
||||
$user->hasRole('writer');
|
||||
$user->hasAnyRole(['writer', 'editor']);
|
||||
$user->hasAllRoles(['writer', 'editor']);
|
||||
$user->hasExactRoles(['writer', 'editor']);
|
||||
|
||||
// Get assigned roles and permissions
|
||||
$user->getRoleNames(); // Collection of role name strings
|
||||
$user->getPermissionNames(); // Collection of permission name strings
|
||||
$user->getDirectPermissions(); // Direct permissions only
|
||||
$user->getPermissionsViaRoles(); // Inherited via roles
|
||||
$user->getAllPermissions(); // Both direct and inherited
|
||||
```
|
||||
|
||||
## Query Scopes
|
||||
|
||||
```php
|
||||
$users = User::role('writer')->get();
|
||||
$users = User::withoutRole('writer')->get();
|
||||
$users = User::permission('edit articles')->get();
|
||||
$users = User::withoutPermission('edit articles')->get();
|
||||
```
|
||||
|
||||
## Middleware
|
||||
|
||||
Register middleware aliases in `bootstrap/app.php`:
|
||||
|
||||
```php
|
||||
->withMiddleware(function (Middleware $middleware) {
|
||||
$middleware->alias([
|
||||
'role' => \Spatie\Permission\Middleware\RoleMiddleware::class,
|
||||
'permission' => \Spatie\Permission\Middleware\PermissionMiddleware::class,
|
||||
'role_or_permission' => \Spatie\Permission\Middleware\RoleOrPermissionMiddleware::class,
|
||||
]);
|
||||
})
|
||||
```
|
||||
|
||||
Use in routes (pipe `|` for OR logic):
|
||||
|
||||
```php
|
||||
Route::middleware(['permission:edit articles'])->group(function () { ... });
|
||||
Route::middleware(['role:manager|writer'])->group(function () { ... });
|
||||
Route::middleware(['role_or_permission:manager|edit articles'])->group(function () { ... });
|
||||
|
||||
// With specific guard
|
||||
Route::middleware(['role:manager,api'])->group(function () { ... });
|
||||
```
|
||||
|
||||
For single permissions, Laravel's built-in `can` middleware also works:
|
||||
|
||||
```php
|
||||
Route::middleware(['can:edit articles'])->group(function () { ... });
|
||||
```
|
||||
|
||||
## Blade Directives
|
||||
|
||||
Prefer `@can` (permission-based) over `@role` (role-based):
|
||||
|
||||
```blade
|
||||
@can('edit articles')
|
||||
{{-- User can edit articles (supports Super Admin) --}}
|
||||
@endcan
|
||||
|
||||
@canany(['edit articles', 'delete articles'])
|
||||
{{-- User can do at least one --}}
|
||||
@endcanany
|
||||
|
||||
@role('admin')
|
||||
{{-- Only use for super-admin type checks --}}
|
||||
@endrole
|
||||
|
||||
@hasanyrole('writer|admin')
|
||||
{{-- Has writer or admin --}}
|
||||
@endhasanyrole
|
||||
```
|
||||
|
||||
## Super Admin
|
||||
|
||||
Use `Gate::before` in `AppServiceProvider::boot()`:
|
||||
|
||||
```php
|
||||
use Illuminate\Support\Facades\Gate;
|
||||
|
||||
public function boot(): void
|
||||
{
|
||||
Gate::before(function ($user, $ability) {
|
||||
return $user->hasRole('Super Admin') ? true : null;
|
||||
});
|
||||
}
|
||||
```
|
||||
|
||||
This makes `$user->can()` and `@can` always return true for Super Admins. Must return `null` (not `false`) to allow normal checks for other users.
|
||||
|
||||
## Policies
|
||||
|
||||
Use `$user->can()` inside policy methods to check permissions:
|
||||
|
||||
```php
|
||||
class PostPolicy
|
||||
{
|
||||
public function update(User $user, Post $post): bool
|
||||
{
|
||||
if ($user->can('edit all posts')) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return $user->can('edit own posts') && $user->id === $post->user_id;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Enums
|
||||
|
||||
```php
|
||||
enum RolesEnum: string
|
||||
{
|
||||
case WRITER = 'writer';
|
||||
case EDITOR = 'editor';
|
||||
}
|
||||
|
||||
enum PermissionsEnum: string
|
||||
{
|
||||
case EDIT_POSTS = 'edit posts';
|
||||
case DELETE_POSTS = 'delete posts';
|
||||
}
|
||||
|
||||
// Creation requires ->value
|
||||
Permission::findOrCreate(PermissionsEnum::EDIT_POSTS->value, 'web');
|
||||
|
||||
// Most methods accept enums directly
|
||||
$user->assignRole(RolesEnum::WRITER);
|
||||
$user->hasRole(RolesEnum::WRITER);
|
||||
$role->givePermissionTo(PermissionsEnum::EDIT_POSTS);
|
||||
$user->hasPermissionTo(PermissionsEnum::EDIT_POSTS);
|
||||
```
|
||||
|
||||
## Seeding
|
||||
|
||||
Always flush the permission cache when seeding:
|
||||
|
||||
```php
|
||||
class RolesAndPermissionsSeeder extends Seeder
|
||||
{
|
||||
public function run(): void
|
||||
{
|
||||
// Reset cache
|
||||
app()[\Spatie\Permission\PermissionRegistrar::class]->forgetCachedPermissions();
|
||||
|
||||
// Create permissions
|
||||
Permission::findOrCreate('edit articles', 'web');
|
||||
Permission::findOrCreate('delete articles', 'web');
|
||||
|
||||
// Create roles and assign permissions
|
||||
Role::findOrCreate('writer', 'web')
|
||||
->givePermissionTo(['edit articles']);
|
||||
|
||||
Role::findOrCreate('admin', 'web')
|
||||
->givePermissionTo(Permission::all());
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Teams (Multi-Tenancy)
|
||||
|
||||
Enable in `config/permission.php` before running migrations:
|
||||
|
||||
```php
|
||||
'teams' => true,
|
||||
```
|
||||
|
||||
Set the active team in middleware:
|
||||
|
||||
```php
|
||||
setPermissionsTeamId($teamId);
|
||||
```
|
||||
|
||||
When switching teams, unset cached relations:
|
||||
|
||||
```php
|
||||
$user->unsetRelation('roles')->unsetRelation('permissions');
|
||||
```
|
||||
|
||||
## Events
|
||||
|
||||
Enable in `config/permission.php`:
|
||||
|
||||
```php
|
||||
'events_enabled' => true,
|
||||
```
|
||||
|
||||
Available events: `RoleAttachedEvent`, `RoleDetachedEvent`, `PermissionAttachedEvent`, `PermissionDetachedEvent` in the `Spatie\Permission\Events` namespace.
|
||||
|
||||
## Performance
|
||||
|
||||
- Permissions are cached automatically. The cache is flushed when roles/permissions change via package methods.
|
||||
- After direct DB operations, flush manually: `app()[\Spatie\Permission\PermissionRegistrar::class]->forgetCachedPermissions()`
|
||||
- For bulk seeding, use `Permission::insert()` for speed, but flush the cache afterward.
|
||||
Loading…
Reference in New Issue
Block a user