diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..e5743dc --- /dev/null +++ b/.gitattributes @@ -0,0 +1,6 @@ +/.vscode export-ignore +/.gitattributes export-ignore +/.gitignore export-ignore +/phpunit.xml.dist export-ignore +/docs export-ignore +/tests export-ignore \ No newline at end of file diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..977478f --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,6 @@ +{ + "editor.formatOnSave": true, + "[php]": { + "editor.defaultFormatter": "bmewburn.vscode-intelephense-client" + }, +} \ No newline at end of file diff --git a/composer.json b/composer.json index 29e473f..eb356c3 100644 --- a/composer.json +++ b/composer.json @@ -32,6 +32,7 @@ }, "require": { "php": "^8.0", + "illuminated/helper-functions": "^8.12|^9.0|^10.0|^11.0|^12.0", "illuminate/auth": "^8.12|^9.0|^10.0|^11.0|^12.0", "illuminate/container": "^8.12|^9.0|^10.0|^11.0|^12.0", "illuminate/contracts": "^8.12|^9.0|^10.0|^11.0|^12.0", @@ -39,21 +40,16 @@ "illuminate/support": "^8.12|^9.0|^10.0|^11.0|^12.0" }, "require-dev": { - "laravel/pint": "^1.22" + "laravel/pint": "^1.22", + "laravel/framework": "*" }, "extra": { "laravel": { "providers": [ - "Blax\\Roles\\PermissionsServiceProvider" + "Blax\\Roles\\RolesServiceProvider" ] } }, "minimum-stability": "dev", - "prefer-stable": true, - "scripts": { - "post-install-cmd": [ - "@php artisan vendor:publish --tag=roles-migrations --force", - "@php artisan vendor:publish --tag=roles-config --force" - ] - } + "prefer-stable": true } \ No newline at end of file diff --git a/config/roles.php b/config/roles.php index 41c8183..b66ab7e 100644 --- a/config/roles.php +++ b/config/roles.php @@ -8,11 +8,13 @@ return [ 'role_permission' => \Blax\Roles\Models\RolePermission::class, 'permission' => \Blax\Roles\Models\Permission::class, 'permission_usage' => \Blax\Roles\Models\PermissionUsage::class, + 'permission_members' => \Blax\Roles\Models\PermissionMember::class, ], 'table_names' => [ 'permissions' => 'permissions', 'permission_usage' => 'permission_usages', + 'permission_members' => 'permission_members', 'roles' => 'roles', 'role_member' => 'role_members', 'role_permission' => 'role_permissions', diff --git a/database/migrations/create_blax_role_tables.php.stub b/database/migrations/create_blax_role_tables.php.stub index a196812..197912a 100644 --- a/database/migrations/create_blax_role_tables.php.stub +++ b/database/migrations/create_blax_role_tables.php.stub @@ -21,15 +21,26 @@ return new class extends Migration $table->timestamps(); }); + // PermissionMember + Schema::create(config('roles.table_names.permission_members'), function (Blueprint $table) { + $table->id(); + $table->foreignId('permission_id')->constrained('permissions')->onDelete('cascade'); + $table->morphs('member'); + $table->json('context')->nullable(); + $table->timestamp('expires_at')->nullable(); + $table->timestamps(); + }); + // PermissionUsage Schema::create(config('roles.table_names.permission_usage'), function (Blueprint $table) { $table->id(); $table->foreignId('permission_id')->constrained('permissions')->onDelete('cascade'); + $table->float('usage', 8)->default(1); $table->morphs('user'); $table->json('context')->nullable(); $table->timestamps(); }); - + // Role Schema::create(config('roles.table_names.roles'), function (Blueprint $table) { $table->id(); @@ -73,6 +84,7 @@ return new class extends Migration Schema::dropIfExists(config('roles.table_names.role_members')); Schema::dropIfExists(config('roles.table_names.roles')); Schema::dropIfExists(config('roles.table_names.permission_usage')); + Schema::dropIfExists(config('roles.table_names.permission_members')); Schema::dropIfExists(config('roles.table_names.permissions')); } }; diff --git a/src/Models/Permission.php b/src/Models/Permission.php index b36628c..5bf86a3 100644 --- a/src/Models/Permission.php +++ b/src/Models/Permission.php @@ -4,7 +4,8 @@ namespace Blax\Roles\Models; use Illuminate\Database\Eloquent\Model; -class Permission extends Model { +class Permission extends Model +{ protected $fillable = [ 'slug', 'description', @@ -26,4 +27,9 @@ class Permission extends Model { { return $this->belongsToMany(RolePermission::class); } + + public function members() + { + return $this->hasMany(PermissionMember::class); + } } diff --git a/src/Models/PermissionMember.php b/src/Models/PermissionMember.php new file mode 100644 index 0000000..df139a6 --- /dev/null +++ b/src/Models/PermissionMember.php @@ -0,0 +1,41 @@ + 'array', + 'expires_at' => 'datetime', + ]; + + public function __construct(array $attributes = []) + { + parent::__construct($attributes); + + $this->table = config('roles.table_names.permission_members') ?: parent::getTable(); + } + + public function permission() + { + return $this->belongsTo(Permission::class); + } + + public function member() + { + return $this->morphTo(); + } +} diff --git a/src/Models/PermissionUsage.php b/src/Models/PermissionUsage.php index 2ba4c83..14e8b1d 100644 --- a/src/Models/PermissionUsage.php +++ b/src/Models/PermissionUsage.php @@ -4,10 +4,19 @@ namespace Blax\Roles\Models; use Illuminate\Database\Eloquent\Model; -class PermissionUsage extends Model { +class PermissionUsage extends Model +{ protected $fillable = [ 'permission_id', - 'usage_count' + 'usage', + 'context', + 'user_type', + 'user_id', + ]; + + protected $casts = [ + 'context' => 'array', + 'usage' => 'float', ]; public function __construct(array $attributes = []) @@ -21,4 +30,9 @@ class PermissionUsage extends Model { { return $this->belongsTo(Permission::class); } + + public function user() + { + return $this->morphTo(); + } } diff --git a/src/Models/Role.php b/src/Models/Role.php index c2e56c1..d7ef57a 100644 --- a/src/Models/Role.php +++ b/src/Models/Role.php @@ -4,10 +4,11 @@ namespace Blax\Roles\Models; use Illuminate\Database\Eloquent\Model; -class Role extends Model { +class Role extends Model +{ protected $fillable = [ 'parent_id', - 'name', + 'name', 'slug', 'description', ]; @@ -19,19 +20,23 @@ class Role extends Model { $this->table = config('roles.table_names.roles') ?: parent::getTable(); } - public function members() { - return $this->belongsToMany(RoleMember::class); + public function members() + { + return $this->hasMany(RoleMember::class, 'role_id', 'id'); } - public function permissions() { - return $this->belongsToMany(RolePermission::class); + public function permissions() + { + return $this->hasMany(Permission::class, 'role_id', 'id'); } - public function parent() { - return $this->belongsTo(Role::class, 'parent_id'); + public function parent() + { + return $this->hasOne(Role::class, 'id', 'parent_id'); } - - public function children() { + + public function children() + { return $this->hasMany(Role::class, 'parent_id'); } } diff --git a/src/Models/RoleMember.php b/src/Models/RoleMember.php index 0ce35b2..7d459d0 100644 --- a/src/Models/RoleMember.php +++ b/src/Models/RoleMember.php @@ -2,9 +2,13 @@ namespace Blax\Roles\Models; +use Blax\Roles\Traits\WillExpire; use Illuminate\Database\Eloquent\Model; -class RoleMember extends Model { +class RoleMember extends Model +{ + use WillExpire; + protected $fillable = [ 'role_id', 'member', @@ -12,6 +16,11 @@ class RoleMember extends Model { 'expires_at', ]; + protected $casts = [ + 'context' => 'array', + 'expires_at' => 'datetime', + ]; + public function __construct(array $attributes = []) { parent::__construct($attributes); @@ -19,11 +28,13 @@ class RoleMember extends Model { $this->table = config('roles.table_names.role_members') ?: parent::getTable(); } - public function role() { + public function role() + { return $this->belongsTo(Role::class); } - - public function member() { + + public function member() + { return $this->morphTo(); } } diff --git a/src/Models/RolePermission.php b/src/Models/RolePermission.php index 06f7058..40dfca9 100644 --- a/src/Models/RolePermission.php +++ b/src/Models/RolePermission.php @@ -2,9 +2,13 @@ namespace Blax\Roles\Models; +use Blax\Roles\Traits\WillExpire; use Illuminate\Database\Eloquent\Model; -class RolePermission extends Model { +class RolePermission extends Model +{ + use WillExpire; + protected $fillable = [ 'role_id', 'permission_id', @@ -17,11 +21,13 @@ class RolePermission extends Model { $this->table = config('roles.table_names.role_permissions') ?: parent::getTable(); } - public function role() { + public function role() + { return $this->belongsTo(Role::class); } - public function permission() { + public function permission() + { return $this->belongsTo(Permission::class); } } diff --git a/src/PermissionsServiceProvider.php b/src/RolesServiceProvider.php similarity index 91% rename from src/PermissionsServiceProvider.php rename to src/RolesServiceProvider.php index b8980d5..3c963fd 100644 --- a/src/PermissionsServiceProvider.php +++ b/src/RolesServiceProvider.php @@ -2,7 +2,7 @@ namespace Blax\Roles; -class PermissionsServiceProvider extends \Illuminate\Support\ServiceProvider +class RolesServiceProvider extends \Illuminate\Support\ServiceProvider { /** * Register the service provider. @@ -73,5 +73,6 @@ class PermissionsServiceProvider extends \Illuminate\Support\ServiceProvider $this->app->bind(\Blax\Roles\Models\RolePermission::class, fn ($app) => $app->make($app->config['roles.models.role_permission'])); $this->app->bind(\Blax\Roles\Models\Permission::class, fn ($app) => $app->make($app->config['roles.models.permission'])); $this->app->bind(\Blax\Roles\Models\PermissionUsage::class, fn ($app) => $app->make($app->config['roles.models.permission_usage'])); + $this->app->bind(\Blax\Roles\Models\PermissionMember::class, fn ($app) => $app->make($app->config['roles.models.permission_members'])); } } diff --git a/src/Traits/HasPermissions.php b/src/Traits/HasPermissions.php new file mode 100644 index 0000000..e1dec9c --- /dev/null +++ b/src/Traits/HasPermissions.php @@ -0,0 +1,27 @@ +permissions() + ->where('name', $permission) + ->where(function ($query) use ($context) { + if (!empty($context)) { + $query->where('context', $context); + } + }) + ->exists(); + } + + public function permissions() + { + return $this->morphToMany( + config('roles.models.permission'), + 'member', + config('roles.table_names.permission_members') + ); + } +} diff --git a/src/Traits/WillExpire.php b/src/Traits/WillExpire.php new file mode 100644 index 0000000..a6ce3b5 --- /dev/null +++ b/src/Traits/WillExpire.php @@ -0,0 +1,22 @@ +where(function ($query) { + $query->whereNull('expires_at') + ->orWhere('expires_at', '>', now()); + }); + }); + } + + + public function isExpired(): bool + { + return $this->expires_at && $this->expires_at->isPast(); + } +}