I role expiration extension

This commit is contained in:
a6a2f5842 2025-10-30 11:23:53 +01:00
parent 84d81863ed
commit a854e85388
2 changed files with 73 additions and 4 deletions

View File

@ -15,14 +15,18 @@ trait HasRoles
*/ */
public function roles() public function roles()
{ {
$pivotTable = config('roles.table_names.role_member', 'role_members');
return $this->morphToMany( return $this->morphToMany(
config('roles.models.role', \Blax\Roles\Models\Role::class), config('roles.models.role', \Blax\Roles\Models\Role::class),
'member', 'member',
config('roles.table_names.role_members', 'role_members') $pivotTable
)->withPivot('expires_at', 'created_at', 'updated_at') )->withPivot('expires_at', 'created_at', 'updated_at')
->withTimestamps() ->withTimestamps()
->wherePivot('expires_at', '>', now()) ->where(function ($q) use ($pivotTable) {
->orWhereNull('expires_at'); $q->wherePivot('expires_at', '>', now())
->orWhereNull($pivotTable . '.expires_at');
});
} }
/** /**
@ -142,6 +146,58 @@ trait HasRoles
return $this; return $this;
} }
/**
* Extend the expiration of an existing role by the given hours, or attach the role
* with an expiration if the member does not already have it.
* If the existing role has no expiration (expires_at is null), it will be left as-is.
*
* @param int|string|Role $role
* @param int $hours
* @return $this
*/
public function extendOrAddRole($role, $hours)
{
$hours = (int) $hours;
if ($hours <= 0) {
return $this;
}
// Resolve role
if (is_string($role) && !is_numeric($role)) {
$role = config('roles.models.role', \Blax\Roles\Models\Role::class)::firstOrCreate([
'name' => $role,
'slug' => str()->slug($role)
]);
} elseif (is_numeric($role)) {
$role = config('roles.models.role', \Blax\Roles\Models\Role::class)::find($role);
} elseif (!$role instanceof Role) {
throw new \InvalidArgumentException('Role must be a string, numeric ID, or an instance of Role.');
}
if (!$role) {
return $this;
}
$roleMemberModel = config('roles.models.role_member', \Blax\Roles\Models\RoleMember::class);
$existing = $roleMemberModel::withoutGlobalScopes()
->where('role_id', $role->id)
->where('member_id', $this->getKey())
->where('member_type', $this->getMorphClass())
->first();
if ($existing) {
// Extend expiry. If it does not expire (null), leave it unchanged.
$existing->extendByHours($hours, false);
} else {
$this->roles()->attach($role->id, [
'expires_at' => now()->addHours($hours),
]);
}
return $this;
}
/** /**
* Checks if the memberable has any of the given roles * Checks if the memberable has any of the given roles
* *

View File

@ -14,9 +14,22 @@ trait WillExpire
}); });
} }
public function isExpired(): bool public function isExpired(): bool
{ {
return $this->expires_at && $this->expires_at->isPast(); return $this->expires_at && $this->expires_at->isPast();
} }
public function extendByHours(int $hours, bool $expire_if_null = false): void
{
if ($this->expires_at === null && !$expire_if_null) {
// Do not add expiration if it does not expire
return;
} elseif ($this->expires_at->isPast()) {
$this->expires_at = now()->addHours($hours);
} else {
$this->expires_at = $this->expires_at->addHours($hours);
}
$this->save();
}
} }