laravel-roles/src/Traits/HasPermissions.php

146 lines
4.5 KiB
PHP
Raw Normal View History

2025-06-16 07:49:36 +00:00
<?php
namespace Blax\Roles\Traits;
use Illuminate\Support\Collection;
2026-02-10 14:22:26 +00:00
use Illuminate\Support\Facades\DB;
2025-06-16 07:49:36 +00:00
trait HasPermissions
{
2026-02-10 14:22:26 +00:00
/**
* Check if the entity has a specific permission.
*
* Supports hierarchical matching: having permission 'lection' also
* grants 'lection.45', 'lection.foo.bar', etc. (parent acts as wildcard).
*/
2025-06-19 12:45:06 +00:00
public function hasPermission(string $permission): bool
2025-06-16 07:49:36 +00:00
{
2025-07-10 08:29:53 +00:00
$allpermissions = $this->permissions();
2026-02-10 14:22:26 +00:00
// Wildcard: '*' grants everything
2025-06-19 12:45:06 +00:00
if ($allpermissions->contains('slug', '*')) {
2026-02-10 14:22:26 +00:00
return true;
}
2025-06-19 12:45:06 +00:00
return $allpermissions->contains(function ($perm) use ($permission) {
2026-02-10 14:22:26 +00:00
// Exact match
if ($perm->slug === $permission) {
return true;
}
// Hierarchical: permission 'lection' grants 'lection.45', 'lection.foo.bar', etc.
if (str_starts_with($permission, $perm->slug . '.')) {
return true;
}
return false;
2025-06-19 12:45:06 +00:00
});
}
2026-02-10 14:22:26 +00:00
/**
* Get permissions inherited through roles.
*
* Resolves: entity role_members (get role IDs) permission_members
* where member_type is a Role permissions.
*/
public function rolePermissions(): Collection
2025-06-19 12:45:06 +00:00
{
2026-02-10 14:22:26 +00:00
$roleModel = config('roles.models.role');
$permissionModel = config('roles.models.permission');
$roleMemberTable = config('roles.table_names.role_member', 'role_members');
$permMemberTable = config('roles.table_names.permission_member', 'permission_members');
// Get role IDs this entity belongs to (via role_members)
$roleIds = DB::table($roleMemberTable)
->where('member_id', $this->getKey())
->where('member_type', $this->getMorphClass())
->where(function ($q) {
$q->whereNull('expires_at')->orWhere('expires_at', '>', now());
})
->pluck('role_id');
if ($roleIds->isEmpty()) {
return collect();
}
// Get permission IDs assigned to those roles (roles are members in permission_members)
$permissionIds = DB::table($permMemberTable)
->whereIn('member_id', $roleIds)
->where('member_type', $roleModel)
->where(function ($q) {
$q->whereNull('expires_at')->orWhere('expires_at', '>', now());
})
->pluck('permission_id')
->unique();
if ($permissionIds->isEmpty()) {
return collect();
}
return $permissionModel::whereIn('id', $permissionIds)->get();
2025-06-19 12:45:06 +00:00
}
2026-02-10 14:22:26 +00:00
/**
* Get permissions directly assigned to this entity (via permission_members morphToMany).
*/
2025-07-10 08:29:53 +00:00
public function individualPermissions()
2025-06-19 12:45:06 +00:00
{
return $this->morphToMany(
config('roles.models.permission'),
'member',
2026-02-10 14:22:26 +00:00
config('roles.table_names.permission_member', 'permission_members')
2025-06-19 12:45:06 +00:00
);
}
2026-02-10 14:22:26 +00:00
/**
* Get all permissions: role-based + directly assigned, deduplicated.
*/
public function permissions(): Collection
2025-07-10 08:29:53 +00:00
{
2026-02-10 14:22:26 +00:00
$rolePerms = $this->rolePermissions();
2025-07-10 08:31:51 +00:00
$directPerms = $this->individualPermissions()->get();
2025-07-10 08:29:53 +00:00
return $rolePerms
->merge($directPerms)
->unique('id');
}
2025-06-19 12:52:09 +00:00
public function assignPermission($permission)
{
$permission_class = config('roles.models.permission');
if (is_numeric($permission)) {
$permission = $permission_class::find($permission);
} elseif (is_string($permission)) {
2025-06-19 13:08:49 +00:00
$permission = $permission_class::firstOrCreate([
'slug' => $permission
]);
2025-06-19 12:45:06 +00:00
}
2026-02-10 14:22:26 +00:00
if (! ($permission instanceof $permission_class)) {
throw new \InvalidArgumentException('Permission must be a string, numeric ID, or an instance of Permission.');
}
2026-02-10 14:22:26 +00:00
$this->individualPermissions()->syncWithoutDetaching($permission);
2026-02-10 14:22:26 +00:00
return true;
}
public function removePermission($permission): bool
{
$permission_class = config('roles.models.permission');
if (is_numeric($permission)) {
$permission = $permission_class::find($permission);
} elseif (is_string($permission)) {
$permission = $permission_class::where('slug', $permission)->first();
2025-06-19 12:45:06 +00:00
}
if ($permission) {
2026-02-10 14:22:26 +00:00
$this->individualPermissions()->detach($permission);
}
2025-06-19 13:08:49 +00:00
return true;
}
2025-06-16 07:49:36 +00:00
}