amdadulhaq/guard-laravel 问题修复 & 功能扩展

解决BUG、新增功能、兼容多环境部署,快速响应你的开发需求

邮箱:yvsm@zunyunkeji.com | QQ:316430983 | 微信:yvsm316

amdadulhaq/guard-laravel

最新稳定版本:v1.0.3

Composer 安装命令:

composer require amdadulhaq/guard-laravel

包简介

Guard is Role and Permission management system for Laravel

README 文档

README

Latest Version on Packagist GitHub Tests Action Status GitHub Code Style Action Status Total Downloads

Guard is a modern Role and Permission management system for Laravel 10, 11, and 12 with PHP 8.2, 8.3, 8.4, and 8.5 support.

Sponsor This Project

If you find Guard Laravel helpful, please consider sponsoring the project. Your support helps maintain and improve the package.

Become a GitHub Sponsor - Any amount is greatly appreciated!

Table of Contents

Features

  • PHP 8.2, 8.3, 8.4, & 8.5 & Laravel 10, 11, & 12 - Modern PHP and Laravel features
  • Role & Permission Management - Create and manage roles with permissions
  • Wildcard Permissions - Use wildcard patterns like posts.* for flexible permission checking
  • Permission Groups - Organize permissions into groups
  • Guarded Roles - Protect certain roles from deletion/modification
  • Custom Middleware - Built-in middleware for route protection
  • Cache Support - Intelligent caching with automatic invalidation
  • Query Scopes - Filter users by roles and permissions
  • Custom Exceptions - Better error messages
  • Enums - Type-safe constants for CacheKey and PermissionType
  • Developer Tools - Includes Pint, Pest, Rector, and Larastan

Requirements

  • PHP: 8.2, 8.3, 8.4, or 8.5 or higher
  • Laravel: 10, 11, or 12
  • Database: MySQL, PostgreSQL, SQLite, or SQL Server

Installation

You can install the package via composer:

composer require amdadulhaq/guard-laravel

You can publish and run the migrations with:

php artisan vendor:publish --tag="guard-migrations"
php artisan migrate

Add HasRoles Trait and UserContract Interface on User Model:

namespace App\Models;

use AmdadulHaq\Guard\Contracts\User as UserContract;
use AmdadulHaq\Guard\HasRoles;

class User extends Authenticatable implements UserContract
{
    use HasRoles;
}

Configuration

Publish the config file:

php artisan vendor:publish --tag="guard-config"

Configuration options:

return [
    'models' => [
        'user' => \App\Models\User::class,
        'role' => \AmdadulHaq\Guard\Models\Role::class,
        'permission' => \AmdadulHaq\Guard\Models\Permission::class,
    ],
    'tables' => [
        'roles' => 'roles',
        'permissions' => 'permissions',
    ],
    'cache' => [
        'permissions_duration' => env('GUARD_PERMISSIONS_CACHE_DURATION', 3600),
        'roles_duration' => env('GUARD_ROLES_CACHE_DURATION', 3600),
        'enabled' => env('GUARD_CACHE_ENABLED', true),
    ],
    'middleware' => [
        'role' => 'role',
        'permission' => 'permission',
        'role_or_permission' => 'role_or_permission',
    ],
    'wildcard' => [
        'enabled' => env('GUARD_WILDCARD_ENABLED', true),
    ],
];

Usage

Creating Roles

use AmdadulHaq\Guard\Models\Role;

$role = Role::create([
    'name' => 'administrator',
    'label' => 'Administrator',
    'description' => 'Full system access',
    'is_guarded' => true,
]);

Creating Permissions

use AmdadulHaq\Guard\Models\Permission;
use AmdadulHaq\Guard\Models\Role;

$permission = Permission::create([
    'name' => 'users.create',
    'label' => 'Create Users',
    'description' => 'Create new user accounts',
    'group' => 'users',
]);

$role->givePermissionTo($permission);

Wildcard Permissions

Create wildcard permissions for easier management:

Permission::create(['name' => 'posts.*', 'label' => 'All Post Permissions']);

// A user with this role can:
$user->hasPermission('posts.create'); // true
$user->hasPermission('posts.update'); // true
$user->hasPermission('posts.delete'); // true

Assigning Permissions to Roles

// Assign a single permission
$role->givePermissionTo($permission);
$role->givePermissionTo('users.create');

// Sync multiple permissions (supports both IDs and names)
$role->syncPermissions([$permission1->id, $permission2->id]);
$role->syncPermissions(['users.create', 'users.edit', 'users.delete']);

// Revoke a permission
$role->revokePermissionTo($permission);
$role->revokePermissionTo('users.delete');

Assigning Roles to Users

use App\Models\User;

$user = User::first();

// Assign by model
$user->assignRole($role);

// Assign by name
$user->assignRole('administrator');

// Sync multiple roles
$user->syncRoles([$role1->id, $role2->id]);

// Revoke a role
$user->revokeRole($role);

Direct User Permissions

Assign permissions directly to users without going through roles:

use App\Models\User;

$user = User::first();

// Give permission directly to user
$user->givePermissionTo('posts.delete');

// Give multiple permissions
$user->givePermissionTo(['posts.create', 'posts.update']);

// Sync user permissions (replaces all existing permissions)
$user->syncPermissions(['posts.create', 'posts.update', 'posts.delete']);

// Revoke specific permission
$user->revokePermissionTo('posts.delete');

// Check if user has direct permission
$user->hasDirectPermission('posts.create'); // true

// Get all direct permissions for user
$user->getDirectPermissions(); // Collection of permissions

// Get all permissions (roles + direct)
$user->getAllPermissions(); // Collection of all permissions

Checking Roles

$user->hasRole('administrator'); // true or false
$user->hasAllRoles(['admin', 'editor']); // true if user has all
$user->hasAnyRole(['admin', 'editor']); // true if user has any

Checking Permissions

$user->hasPermission('users.create'); // Check by name
$user->hasPermission($permissionModel); // Check by model

// Get all permissions from user's roles
$allPermissions = $user->getPermissions();

Advanced Permission Checking

Check multiple permissions at once with different logic:

// Check if user has ALL specified permissions
$user->hasAllPermissions(['users.create', 'users.edit']); // true only if user has both

// Check if user has ANY of the specified permissions
$user->hasAnyPermission(['users.delete', 'users.edit']); // true if user has at least one

// Check using permission models
$permission1 = Permission::where('name', 'users.create')->first();
$permission2 = Permission::where('name', 'users.edit')->first();
$user->hasAllPermissions([$permission1, $permission2]);

// Check permissions via wildcard
$user->hasPermission('posts.*'); // true if user has posts.create, posts.edit, etc.

// Get all role names for a user
$user->getRoleNames(); // ['administrator', 'editor']

// Get permission names grouped by role
$permissionsByRole = $user->getPermissionsByRole();
// [
//     'administrator' => ['users.create', 'users.delete'],
//     'editor' => ['posts.create', 'posts.edit']
// ]

Query Scopes

Filter users by roles and permissions:

// Get users with specific role
User::withRoles('administrator')->get();

// Get users with specific permission
User::withPermissions('users.create')->get();

Middleware

Protect routes using built-in middleware:

Route::middleware('permission:users.create')->group(function () {
    Route::post('/users', [UserController::class, 'store']);
});

Route::middleware('role:administrator')->group(function () {
    Route::delete('/users/{id}', [UserController::class, 'destroy']);
});

Route::middleware('role_or_permission:users.create')->group(function () {
    Route::get('/users', [UserController::class, 'index']);
});

Gate Integration

Use Laravel's Gate and authorization:

// Using Gate facade
Gate::authorize('users.create');
Gate::authorize('administrator');

// Using controller helper
$this->authorize('users.create');

// Check via Gate facade
if (Gate::allows('users.create')) {
    // User can create users
}

if (Gate::denies('users.delete')) {
    abort(403);
}

// Using Gate::forUser for a specific user
if (Gate::forUser($user)->allows('posts.edit')) {
    // This specific user can edit posts
}

Blade Directives

Use Blade directives for conditional rendering in your views:

@can('users.create')
    <button>Create User</button>
@elsecan('users.edit')
    <button>Edit Users</button>
@else
    <p>No permission</p>
@endcan

@canany(['users.create', 'users.edit'])
    <p>You can create or edit users</p>
@endcanany

@cannot('users.delete')
    <p>You cannot delete users</p>
@endcannot

@role('administrator')
    <p>Admin content - only visible to administrators</p>
@endrole

@hasrole('administrator')
    <p>Alternative syntax for role check</p>
@endhasrole

@hasanyrole(['administrator', 'editor'])
    <p>Visible to admins and editors</p>
@endhasanyrole

@hasallroles(['administrator', 'moderator'])
    <p>Visible only to users with both roles</p>
@endhasallroles

Permission Helpers

Get permission information:

$permission->isWildcard(); // Check if permission is wildcard
$permission->getGroup(); // Get permission group (e.g., 'users')
$permission->getType(); // Get permission type enum

Role Helpers

$role->isProtectedRole(); // Check if role is protected
$role->getPermissionNames(); // Get all permission names for role

// Query scopes
Role::guarded()->get(); // Get all guarded roles
Role::unguarded()->get(); // Get all unguarded roles

Cache Management

The package automatically caches permissions and roles. Clear cache manually:

use AmdadulHaq\Guard\GuardServiceProvider;

GuardServiceProvider::staticClearCache();

Cache is automatically invalidated when roles or permissions are created, updated, or deleted.

Custom Exceptions

use AmdadulHaq\Guard\Exceptions\PermissionDeniedException;
use AmdadulHaq\Guard\Exceptions\RoleDoesNotExistException;

if (!$user->hasPermission('users.delete')) {
    throw PermissionDeniedException::create('users.delete');
}

$role = Role::where('name', 'non-existent')->first();
if (!$role) {
    throw RoleDoesNotExistException::named('admin');
}

Permission Type Enum

use AmdadulHaq\Guard\Enums\PermissionType;

PermissionType::CREATE->label(); // "Create"
PermissionType::DELETE->label(); // "Delete"
PermissionType::VIEW_ANY->label(); // "View any"
PermissionType::UPDATE->label(); // "Update"
PermissionType::RESTORE->label(); // "Restore"
PermissionType::FORCE_DELETE->label(); // "Force delete"

Seeding Roles and Permissions

Create seeders to set up default roles and permissions:

<?php

namespace Database\Seeders;

use AmdadulHaq\Guard\Models\Permission;
use AmdadulHaq\Guard\Models\Role;
use Illuminate\Database\Seeder;

class RoleAndPermissionSeeder extends Seeder
{
    public function run(): void
    {
        // Create roles
        $admin = Role::create(['name' => 'admin', 'label' => 'Administrator', 'is_guarded' => true]);
        $editor = Role::create(['name' => 'editor', 'label' => 'Editor']);
        $user = Role::create(['name' => 'user', 'label' => 'User']);

        // Create permissions
        $permissions = [
            ['name' => 'users.view_any', 'label' => 'View All Users', 'group' => 'users'],
            ['name' => 'users.create', 'label' => 'Create User', 'group' => 'users'],
            ['name' => 'users.edit', 'label' => 'Edit User', 'group' => 'users'],
            ['name' => 'users.delete', 'label' => 'Delete User', 'group' => 'users'],
            ['name' => 'posts.view_any', 'label' => 'View All Posts', 'group' => 'posts'],
            ['name' => 'posts.create', 'label' => 'Create Post', 'group' => 'posts'],
            ['name' => 'posts.edit', 'label' => 'Edit Post', 'group' => 'posts'],
            ['name' => 'posts.delete', 'label' => 'Delete Post', 'group' => 'posts'],
            ['name' => 'posts.*', 'label' => 'Manage All Posts', 'group' => 'posts', 'is_wildcard' => true],
        ];

        foreach ($permissions as $permission) {
            Permission::create($permission);
        }

        // Assign permissions to roles
        $admin->syncPermissions(Permission::all()->pluck('id')->toArray());
        $editor->syncPermissions(['posts.view_any', 'posts.create', 'posts.edit', 'posts.*']);
        $user->syncPermissions(['posts.view_any']);
    }
}

Run the seeder:

php artisan db:seed --class=RoleAndPermissionSeeder

API Protection

Protect API routes with role and permission middleware:

// routes/api.php
use Illuminate\Support\Facades\Route;

// Public routes
Route::post('/login', [AuthController::class, 'login']);
Route::post('/register', [AuthController::class, 'register']);

// Protected routes - require authentication
Route::middleware('auth:sanctum')->group(function () {
    // User routes
    Route::middleware('permission:users.view_any')->get('/users', [UserController::class, 'index']);
    Route::middleware('permission:users.create')->post('/users', [UserController::class, 'store']);
    Route::middleware('permission:users.edit')->put('/users/{id}', [UserController::class, 'update']);
    Route::middleware('permission:users.delete')->delete('/users/{id}', [UserController::class, 'destroy']);

    // Post routes
    Route::middleware('permission:posts.view_any')->get('/posts', [PostController::class, 'index']);
    Route::middleware('permission:posts.create')->post('/posts', [PostController::class, 'store']);
    Route::middleware('permission:posts.edit')->put('/posts/{id}', [PostController::class, 'update']);
    Route::middleware('permission:posts.delete')->delete('/posts/{id}', [PostController::class, 'destroy']);

    // Admin only routes
    Route::middleware('role:admin')->prefix('/admin')->group(function () {
        Route::get('/dashboard', [AdminController::class, 'dashboard']);
        Route::get('/settings', [AdminController::class, 'settings']);
        Route::post('/settings', [AdminController::class, 'updateSettings']);
    });

    // Multiple permissions required
    Route::middleware(['permission:posts.edit', 'permission:posts.publish'])
        ->post('/posts/{id}/publish', [PostController::class, 'publish']);
});

// Using role_or_permission middleware
Route::middleware(['auth:sanctum', 'role_or_permission:admin,posts.manage'])
    ->delete('/posts/{id}', [PostController::class, 'destroy']);

Example API Controller with authorization:

<?php

namespace App\Http\Controllers;

use App\Models\Post;
use App\Http\Requests\PostRequest;
use Illuminate\Http\Request;
use Illuminate\Http\JsonResponse;

class PostController extends Controller
{
    public function index(): JsonResponse
    {
        $this->authorize('posts.view_any');

        $posts = Post::with('author')->latest()->paginate(20);
        return response()->json($posts);
    }

    public function store(PostRequest $request): JsonResponse
    {
        $this->authorize('posts.create');

        $post = auth()->user()->posts()->create($request->validated());
        return response()->json($post, 201);
    }

    public function update(PostRequest $request, Post $post): JsonResponse
    {
        $this->authorize('posts.edit', $post);

        $post->update($request->validated());
        return response()->json($post);
    }

    public function destroy(Post $post): JsonResponse
    {
        $this->authorize('posts.delete', $post);

        $post->delete();
        return response()->json(null, 204);
    }
}

Database Structure

Roles Table

Schema::create('roles', function (Blueprint $table) {
    $table->id();
    $table->string('name')->unique();
    $table->string('label')->nullable();
    $table->text('description')->nullable();
    $table->boolean('is_guarded')->default(false);
    $table->timestamps();
});

Permissions Table

Schema::create('permissions', function (Blueprint $table) {
    $table->id();
    $table->string('name')->unique();
    $table->string('label')->nullable();
    $table->text('description')->nullable();
    $table->string('group')->nullable();
    $table->boolean('is_wildcard')->default(false);
    $table->timestamps();
});

Permission-Role Pivot Table

Schema::create('permission_role', function (Blueprint $table) {
    $table->foreignId('permission_id')->constrained('permissions')->cascadeOnDelete();
    $table->foreignId('role_id')->constrained('roles')->cascadeOnDelete();
    $table->primary(['permission_id', 'role_id']);
});

Role-User Pivot Table

Schema::create('role_user', function (Blueprint $table) {
    $table->foreignId('role_id')->constrained('roles')->cascadeOnDelete();
    $table->foreignId('user_id')->constrained('users')->cascadeOnDelete();
    $table->primary(['role_id', 'user_id']);
});

Available Enums

CacheKey

enum CacheKey: string
{
    case PERMISSIONS = 'guard_permissions';
    case ROLES = 'guard_roles';
}

PermissionType

enum PermissionType: string
{
    case READ = 'read';
    case WRITE = 'write';
    case DELETE = 'delete';
    case MANAGE = 'manage';
    case VIEW_ANY = 'view_any';
    case VIEW = 'view';
    case CREATE = 'create';
    case UPDATE = 'update';
    case RESTORE = 'restore';
    case FORCE_DELETE = 'force_delete';

    public function label(): string
    {
        return str_replace('_', ' ', ucfirst($this->value));
    }
}

Development Tools

This package includes comprehensive developer tools:

Code Refactoring

# Run Rector
composer refactor

# Check Rector dry-run
composer refactor:check

Code Quality

# Format code with Laravel Pint
composer lint

# Check code style
composer lint:check

Testing

# Run Pest tests
composer test

# Run tests with coverage
composer test-coverage

Static Analysis

# Run Larastan
composer analyse

Testing

Run the test suite:

composer test

For code coverage:

composer test-coverage

Contributing

Please see CONTRIBUTING for details.

Changelog

Please see CHANGELOG for more information on what has changed recently.

Security Vulnerabilities

Please review our security policy on how to report security vulnerabilities.

Credits

Contributors

License

The MIT License (MIT). Please see License File for more information.

统计信息

  • 总下载量: 2.33k
  • 月度下载量: 0
  • 日度下载量: 0
  • 收藏数: 12
  • 点击次数: 1
  • 依赖项目数: 0
  • 推荐数: 0

GitHub 信息

  • Stars: 12
  • Watchers: 1
  • Forks: 2
  • 开发语言: PHP

其他信息

  • 授权协议: MIT
  • 更新时间: 2024-10-19