1. Go to this page and download the library: Download litepie/shield library. Choose the download type require.
2. Extract the ZIP file and open the index.php.
3. Add this code to the index.php.
<?php
require_once('vendor/autoload.php');
/* Start to develop here. Best regards https://php-download.com/ */
// Assign single role
$user->assignRole('writer');
$user->assignRole($writerRole);
// Assign multiple roles
$user->assignRole(['writer', 'editor']);
$user->assignRole([$writerRole, $editorRole]);
// Sync roles (removes all other roles)
$user->syncRoles(['admin', 'editor']);
// Remove roles
$user->removeRole('writer');
$user->removeRole(['writer', 'editor']);
// Give permission directly to user
$user->givePermissionTo('edit articles');
$user->givePermissionTo(['edit articles', 'delete articles']);
// Using permission object
$user->givePermissionTo($permission);
// Sync permissions
$user->syncPermissions(['edit articles', 'view dashboard']);
// Revoke permissions
$user->revokePermissionTo('edit articles');
$user->revokePermissionTo(['edit articles', 'delete articles']);
// Laravel's built-in authorization
$user->can('edit articles');
$user->cannot('delete articles');
// Package-specific methods
$user->hasPermissionTo('edit articles');
$user->hasDirectPermission('edit articles'); // Only direct permissions
$user->hasPermissionViaRole('edit articles'); // Only via roles
// Check multiple permissions
$user->hasAnyPermission(['edit articles', 'delete articles']);
$user->hasAllPermissions(['edit articles', 'view dashboard']);
// Check if user has role
$user->hasRole('writer');
$user->hasRole($writerRole);
// Check multiple roles
$user->hasAnyRole(['writer', 'editor']);
$user->hasAllRoles(['writer', 'editor']);
$user->hasExactRoles(['writer', 'editor']); // Only these roles, no more
// Get user roles
$roles = $user->roles; // Collection of roles
$roleNames = $user->getRoleNames(); // Collection of role names
// Check with specific guard
$user->hasRole('admin', 'web');
$user->hasPermissionTo('access-api', 'api');
// Get permissions
$permissions = $user->permissions; // Direct permissions
$allPermissions = $user->getAllPermissions(); // Direct + via roles
$rolePermissions = $user->getPermissionsViaRoles(); // Only via roles
// Check if user has any permissions
if ($user->permissions->isNotEmpty()) {
// User has some permissions
}
use Litepie\Shield\Models\Permission as ShieldPermission;
class Permission extends ShieldPermission
{
protected $fillable = ['name', 'guard_name', 'description', 'category'];
public function category()
{
return $this->belongsTo(PermissionCategory::class);
}
public function scopeByCategory($query, $category)
{
return $query->where('category', $category);
}
}
use Litepie\Shield\Contracts\PermissionsTenantResolver;
class CustomTenantResolver implements PermissionsTenantResolver
{
public function getPermissionsTenantId(): ?int
{
// Get from authenticated user
return auth()->user()?->current_tenant_id;
// Or from request header
return request()->header('X-Tenant-ID');
// Or from subdomain
$subdomain = request()->getHost();
return Tenant::where('subdomain', $subdomain)->value('id');
}
public function setPermissionsTenantId(?int $tenantId): void
{
if ($user = auth()->user()) {
$user->update(['current_tenant_id' => $tenantId]);
}
session(['current_tenant_id' => $tenantId]);
}
}
class RequirePermissionMiddleware
{
public function handle($request, Closure $next, $permission, $guard = null)
{
$user = Auth::guard($guard)->user();
if (!$user || !$user->can($permission)) {
if ($request->expectsJson()) {
return response()->json(['error' => 'Forbidden'], 403);
}
abort(403, 'You do not have permission to access this resource.');
}
return $next($request);
}
}
'tenants' => true,
// Set tenant context
setPermissionsTenantId(1);
// Now all permission checks will be scoped to tenant 1
$user->hasPermissionTo('edit articles'); // Only checks tenant 1 permissions
// Get current tenant
$tenantId = getPermissionsTenantId();
/** @test */
public function middleware_blocks_unauthorized_access()
{
$user = User::factory()->create();
// Without permission
$this->actingAs($user)
->get('/admin/users')
->assertStatus(403);
// With permission
$user->givePermissionTo('manage users');
$this->actingAs($user)
->get('/admin/users')
->assertStatus(200);
}
/** @test */
public function permission_checks_are_cached()
{
$user = User::factory()->create();
$user->givePermissionTo('test permission');
// First check (loads from database)
$start = microtime(true);
$user->can('test permission');
$firstCheckTime = microtime(true) - $start;
// Second check (loads from cache)
$start = microtime(true);
$user->can('test permission');
$secondCheckTime = microtime(true) - $start;
// Cache should be significantly faster
$this->assertLessThan($firstCheckTime / 2, $secondCheckTime);
}
// Create a migration command
php artisan make:command MigrateFromSpatie
class MigrateFromSpatie extends Command
{
protected $signature = 'shield:migrate-from-spatie';
protected $description = 'Migrate from Spatie Laravel Permission to Litepie Shield';
public function handle()
{
$this->info('Migrating from Spatie to Litepie Shield...');
// Update namespace references
$this->updateModelReferences();
// Update config references
$this->updateConfigReferences();
// Update database references if needed
$this->updateDatabaseReferences();
$this->info('Migration completed successfully!');
}
protected function updateModelReferences()
{
// Replace Spatie namespace with Litepie
$files = [
'app/Models/User.php',
'config/permission.php',
// Add other files as needed
];
foreach ($files as $file) {
if (File::exists($file)) {
$content = File::get($file);
$content = str_replace(
'Spatie\Permission',
'Litepie\Shield',
$content
);
File::put($file, $content);
}
}
}
}
// Users with specific role
$admins = User::role('admin')->get();
$editors = User::role(['editor', 'author'])->get();
// Users with specific permission
$canEdit = User::permission('edit articles')->get();
$canPublish = User::permission(['publish articles', 'edit articles'])->get();
// Users without specific role
$nonAdmins = User::withoutRole('admin')->get();
$notManagement = User::withoutRole(['admin', 'manager'])->get();
// Users without specific permission
$cannotDelete = User::withoutPermission('delete articles')->get();
// Complex combinations
$contentTenants = User::role(['editor', 'author'])
->permission('edit articles')
->where('active', true)
->get();
// Users with any of the specified roles
$management = User::hasAnyRole(['admin', 'manager', 'supervisor'])->get();
// Users with all specified roles
$superUsers = User::hasAllRoles(['admin', 'superuser'])->get();
// Roles with specific permissions
$rolesWithEditAccess = Role::whereHas('permissions', function ($query) {
$query->where('name', 'edit articles');
})->get();
// Permissions belonging to specific roles
$adminPermissions = Permission::whereHas('roles', function ($query) {
$query->where('name', 'admin');
})->get();
// Unused permissions
$unusedPermissions = Permission::doesntHave('roles')
->doesntHave('users')
->get();
// Most common roles
$popularRoles = Role::withCount('users')
->orderBy('users_count', 'desc')
->get();
// Tenant-specific queries (when tenants enabled)
setPermissionsTenantId(1);
$tenantRoles = Role::where('tenant_id', 1)->get();
$tenantPermissions = Permission::where('tenant_id', 1)->get();
// Users who can perform specific action
$usersWhoCanEdit = User::whereHas('roles.permissions', function ($query) {
$query->where('name', 'edit articles');
})->orWhereHas('permissions', function ($query) {
$query->where('name', 'edit articles');
})->get();
// Roles that grant specific permission
$rolesWithPermission = Role::whereHas('permissions', function ($query) {
$query->where('name', 'like', 'admin.%');
})->get();
// Permission usage statistics
$permissionStats = Permission::withCount(['roles', 'users'])
->get()
->map(function ($permission) {
return [
'name' => $permission->name,
'total_users' => $permission->users_count +
$permission->roles->sum('users_count'),
'direct_assignments' => $permission->users_count,
'role_assignments' => $permission->roles_count,
];
});
// config/shield.php
'events_enabled' => true,
use Litepie\Shield\Events\RoleAttached;
use Litepie\Shield\Events\RoleDetached;
use Litepie\Shield\Events\PermissionAttached;
use Litepie\Shield\Events\PermissionDetached;
class SendRoleAssignmentNotification
{
public function handle(RoleAttached $event)
{
$user = $event->model;
$role = $event->role;
// Send notification
$user->notify(new RoleAssignedNotification($role));
// Log the change
Log::info("Role '{$role->name}' assigned to user {$user->id}");
// Update external systems
if ($role->name === 'admin') {
ExternalApi::grantAdminAccess($user);
}
}
}
class LogPermissionChange
{
public function handle($event)
{
$user = $event->model;
$permission = $event->permission;
$action = $event instanceof PermissionAttached ? 'granted' : 'revoked';
Log::channel('security')->info("Permission '{$permission->name}' {$action} for user {$user->id}");
// Store audit trail
AuditLog::create([
'user_id' => $user->id,
'action' => "permission_{$action}",
'details' => [
'permission' => $permission->name,
'guard' => $permission->guard_name,
'timestamp' => now(),
],
]);
}
}
// Broadcast role changes
class BroadcastRoleChange
{
public function handle(RoleAttached $event)
{
broadcast(new UserRoleUpdated($event->model, $event->role));
}
}
// WebSocket event
class UserRoleUpdated implements ShouldBroadcast
{
public $user;
public $role;
public function __construct($user, $role)
{
$this->user = $user;
$this->role = $role;
}
public function broadcastOn()
{
return new PrivateChannel("user.{$this->user->id}");
}
}
// Clear all permission cache
app(\Litepie\Shield\PermissionRegistrar::class)->forgetCachedPermissions();
// Automatically cleared when:
// - Roles or permissions are created/updated/deleted
// - User roles are assigned/removed
// - User permissions are granted/revoked
// Manual cache operations
Cache::tags(['shield'])->flush(); // Clear all Shield cache
Cache::forget('shield.permissions'); // Clear specific cache key
// 1. Eager load relationships
$users = User::with(['roles', 'permissions'])->get();
// 2. Use specific permission checks
$user->hasDirectPermission('edit articles'); // Faster than checking via roles
// 3. Cache complex queries
$adminUsers = Cache::remember('admin_users', 3600, function () {
return User::role('admin')->get();
});
// 4. Use database indexes
// Add to migration:
$table->index(['model_type', 'model_id']); // For polymorphic relations
$table->index('tenant_id'); // For tenant-based permissions
// 5. Optimize wildcard permissions
$user->givePermissionTo('posts.*'); // Better than multiple specific permissions