diff --git a/src/Models/Role.php b/src/Models/Role.php index d7ef57a..e654200 100644 --- a/src/Models/Role.php +++ b/src/Models/Role.php @@ -2,10 +2,13 @@ namespace Blax\Roles\Models; +use Blax\Roles\Traits\HasPermissions; use Illuminate\Database\Eloquent\Model; class Role extends Model { + use HasPermissions; + protected $fillable = [ 'parent_id', 'name', @@ -25,11 +28,6 @@ class Role extends Model return $this->hasMany(RoleMember::class, 'role_id', 'id'); } - public function permissions() - { - return $this->hasMany(Permission::class, 'role_id', 'id'); - } - public function parent() { return $this->hasOne(Role::class, 'id', 'parent_id'); diff --git a/src/Traits/HasPermissions.php b/src/Traits/HasPermissions.php index c4f5f2c..24925d9 100644 --- a/src/Traits/HasPermissions.php +++ b/src/Traits/HasPermissions.php @@ -6,47 +6,46 @@ use Illuminate\Support\Collection; trait HasPermissions { + public function can(string $permission): bool + { + return $this->hasPermission($permission); + } + public function hasPermission(string $permission): bool { - return $this->permissions() - ->where('name', $permission) - ->orWhere('slug', '*') - ->exists(); + $allpermissions = $this->allPermissions(); + + if ($allpermissions->contains('slug', '*')) { + return true; // If any permission is '*', all permissions are granted + } + + return $allpermissions->contains(function ($perm) use ($permission) { + return $perm->slug === $permission || $perm->name === $permission; + }); + } + + public function rolePermissions() + { + return $this->hasManyThrough( + config('roles.models.permission'), + config('roles.models.role_member'), + 'member_id', + 'id', + 'id', + 'role_id' + ); } public function permissions() { - $permissionClass = config('roles.models.permission'); - $permissionTable = config('roles.table_names.permissions'); - $permissionMemberTable = config('roles.table_names.permission_member'); - - // direct assignment - $direct = $this->morphToMany( - $permissionClass, + return $this->morphToMany( + config('roles.models.permission'), 'member', - $permissionMemberTable + config('roles.table_names.permission_member', 'permission_member') ); - - if (! method_exists($this, 'roles')) { - return $direct; - } - - // inherited via roles - $permissionRoleTable = config('roles.table_names.permission_role'); - $roleMemberTable = config('roles.table_names.role_member'); - $memberType = $this->getMorphClass(); - - $viaRoles = $permissionClass::query() - ->select("$permissionTable.*") - ->join($permissionRoleTable, "$permissionTable.id", '=', "$permissionRoleTable.permission_id") - ->join($roleMemberTable, "$permissionRoleTable.role_id", '=', "$roleMemberTable.role_id") - ->where("$roleMemberTable.member_id", $this->getKey()) - ->where("$roleMemberTable.member_type", $memberType); - - return $direct->union($viaRoles); } - public function addPermission($permission): bool + public function addPermission($permission) { $permission_class = config('roles.models.permission'); @@ -54,14 +53,16 @@ trait HasPermissions $permission = $permission_class::find($permission); } elseif (is_string($permission)) { $permission = $permission_class::where('slug', $permission)->firstOrCreate(); - } elseif ($permission instanceof $permission_class) { + } + + if ($permission instanceof $permission_class) { // Already a Permission instance } else { throw new \InvalidArgumentException('Permission must be a string, numeric ID, or an instance of Permission.'); } if ($permission) { - return $this->permissions()->attach($permission); + return $this->permissions()->syncWithoutDetaching($permission); } return false; @@ -75,7 +76,9 @@ trait HasPermissions $permission = $permission_class::find($permission); } elseif (is_string($permission)) { $permission = $permission_class::where('slug', $permission)->first(); - } elseif ($permission instanceof $permission_class) { + } + + if ($permission instanceof $permission_class) { // Already a Permission instance } else { throw new \InvalidArgumentException('Permission must be a string, numeric ID, or an instance of Permission.'); @@ -93,7 +96,7 @@ trait HasPermissions * * @return Collection */ - public function allPermissions(): Collection + public function allPermissions() { // Directly assigned permissions $direct = $this->permissions()->get(); diff --git a/src/Traits/HasRoles.php b/src/Traits/HasRoles.php index 2f4b6ec..2db99d3 100644 --- a/src/Traits/HasRoles.php +++ b/src/Traits/HasRoles.php @@ -47,9 +47,9 @@ trait HasRoles /** * Assigns the role to the memberable - * + * * @param int|string|Role $role - * + * * @return $this */ public function assignRole(string|Role $role) @@ -58,7 +58,9 @@ trait HasRoles $role = config('roles.models.role', \Blax\Roles\Models\Role::class)::where('slug', $role)->first(); } elseif (is_numeric($role)) { $role = config('roles.models.role', \Blax\Roles\Models\Role::class)::find($role); - } elseif ($role instanceof Role) { + } + + if ($role instanceof Role) { $this->roles()->attach($role); } else { throw new \InvalidArgumentException('Role must be a string, numeric ID, or an instance of Role.'); @@ -69,9 +71,9 @@ trait HasRoles /** * Removes the role from the memberable - * + * * @param int|string|Role $role - * + * * @return $this */ public function removeRole(string|Role $role) @@ -91,9 +93,9 @@ trait HasRoles /** * Syncs the roles for the memberable - * + * * @param array $roles - * + * * @return $this */ public function syncRoles(array $roles) @@ -126,9 +128,9 @@ trait HasRoles /** * Checks if the memberable has any of the given roles - * + * * @param array $roles - * + * * @return bool */ public function hasAnyRole(array $roles): bool @@ -143,9 +145,9 @@ trait HasRoles /** * Checks if the memberable has all of the given roles - * + * * @param array $roles - * + * * @return bool */ public function hasAllRoles(array $roles): bool