diff --git a/src/Models/Roster.php b/src/Models/Roster.php index 17edd75..96d65d7 100644 --- a/src/Models/Roster.php +++ b/src/Models/Roster.php @@ -53,15 +53,6 @@ final class Roster extends Model public $timestamps = true; - protected $fillable = [ - 'assignable_type', - 'assignable_id', - 'roleable_type', - 'roleable_id', - 'role_key', - 'tenant_id', - ]; - protected $casts = [ 'role_key' => RoleCast::class, ]; @@ -77,6 +68,21 @@ public function __construct(array $attributes = []) $this->conditionallyLoadRelations(); } + /** + * @return string[] + */ + public function getFillable(): array + { + return [ + 'assignable_type', + 'assignable_id', + 'roleable_type', + 'roleable_id', + 'role_key', + config('porter.multitenancy.tenant_column', 'tenant_id'), + ]; + } + /** * Conditionally load relations based on configuration and environment. */ diff --git a/src/RoleManager.php b/src/RoleManager.php index 2b074f0..8ca8d18 100644 --- a/src/RoleManager.php +++ b/src/RoleManager.php @@ -68,7 +68,7 @@ public function assign(AssignableEntity $user, RoleableEntity $target, string|Ro $assignmentData[$tenantColumn] = $tenantId; } - $assignment = Roster::firstOrCreate($assignmentData); + $assignment = $this->resolveRosterModel()::firstOrCreate($assignmentData); if ($assignment->wasRecentlyCreated) { RoleAssigned::dispatch($user, $target, $roleInstance); @@ -141,7 +141,7 @@ public function getParticipantsHasRole(RoleableEntity $target, string|RoleContra $roleInstance = $this->resolveRole($role); $encryptedKey = $roleInstance::getDbKey(); - return Roster::where([ + return $this->resolveRosterModel()::where([ 'roleable_type' => $target->getMorphClass(), 'roleable_id' => $target->getKey(), 'role_key' => $encryptedKey, @@ -158,7 +158,7 @@ public function getParticipantsHasRole(RoleableEntity $target, string|RoleContra */ public function getAssignedEntitiesByKeysByType(AssignableEntity $target, array $keys, string $type): Collection { - return Roster::query()->where([ + return $this->resolveRosterModel()::query()->where([ 'assignable_type' => $target->getMorphClass(), 'assignable_id' => $target->getKey(), 'roleable_type' => $type, @@ -180,7 +180,7 @@ public function getAssignedEntitiesByKeysByType(AssignableEntity $target, array public function getAssignedEntitiesByType(AssignableEntity $entity, string $type): Collection { if (! RoleCacheManager::isEnabled()) { - return Roster::where([ + return $this->resolveRosterModel()::where([ 'roleable_type' => $type, 'assignable_type' => $entity->getMorphClass(), 'assignable_id' => $entity->getKey(), @@ -191,11 +191,14 @@ public function getAssignedEntitiesByType(AssignableEntity $entity, string $type $cacheKey = RoleCacheManager::generateAssignedEntitiesCacheKey($entity, $type); - return RoleCacheManager::remember($cacheKey, RoleCacheManager::getTtl('assigned_entities'), fn () => Roster::where([ - 'roleable_type' => $type, - 'assignable_type' => $entity->getMorphClass(), - 'assignable_id' => $entity->getKey(), - ]) + return RoleCacheManager::remember( + $cacheKey, + RoleCacheManager::getTtl('assigned_entities'), + fn () => $this->resolveRosterModel()::where([ + 'roleable_type' => $type, + 'assignable_type' => $entity->getMorphClass(), + 'assignable_id' => $entity->getKey(), + ]) ->with('roleable') ->get() ->pluck('roleable')); @@ -211,7 +214,7 @@ public function getAssignedEntitiesByType(AssignableEntity $entity, string $type public function getParticipantsWithRoles(RoleableEntity $target): Collection { if (! RoleCacheManager::isEnabled()) { - return Roster::where([ + return $this->resolveRosterModel()::where([ 'roleable_id' => $target->getKey(), 'roleable_type' => $target->getMorphClass(), ]) @@ -222,7 +225,7 @@ public function getParticipantsWithRoles(RoleableEntity $target): Collection return RoleCacheManager::remember( RoleCacheManager::generateParticipantsCacheKey($target), RoleCacheManager::getTtl('participants'), - fn () => Roster::where([ + fn () => $this->resolveRosterModel()::where([ 'roleable_id' => $target->getKey(), 'roleable_type' => $target->getMorphClass(), ]) @@ -268,7 +271,7 @@ public function hasRoleOn(AssignableEntity $user, RoleableEntity $target, string */ public function hasAnyRoleOn(AssignableEntity $user, RoleableEntity $target): bool { - return Roster::where([ + return $this->resolveRosterModel()::where([ 'assignable_id' => $user->getKey(), 'assignable_type' => $user->getMorphClass(), 'roleable_id' => $target->getKey(), @@ -285,7 +288,7 @@ public function hasAnyRoleOn(AssignableEntity $user, RoleableEntity $target): bo */ public function getRoleOn(AssignableEntity $user, RoleableEntity $target): ?RoleContract { - $roster = Roster::where([ + $roster = $this->resolveRosterModel()::where([ 'assignable_id' => $user->getKey(), 'assignable_type' => $user->getMorphClass(), 'roleable_type' => $target->getMorphClass(), @@ -381,7 +384,7 @@ public function bulkClearCache(Collection $targets): void */ private function removeWithinTransaction(AssignableEntity $user, RoleableEntity $target): void { - $assignments = Roster::where([ + $assignments = $this->resolveRosterModel()::where([ 'assignable_type' => $user->getMorphClass(), 'assignable_id' => $user->getKey(), 'roleable_id' => $target->getKey(), @@ -396,7 +399,7 @@ private function removeWithinTransaction(AssignableEntity $user, RoleableEntity } $assignmentIds = $assignments->pluck('id'); - Roster::whereIn('id', $assignmentIds)->delete(); + $this->resolveRosterModel()::whereIn('id', $assignmentIds)->delete(); foreach ($assignments as $assignment) { $encryptedKey = $assignment->getRoleDBKey(); @@ -441,7 +444,7 @@ private function performRoleCheck(AssignableEntity $user, RoleableEntity $target private function executeRoleCheck(AssignableEntity $user, RoleableEntity $target, string $encryptedKey): bool { // First try exact match (most common case) - $exists = Roster::where([ + $exists = $this->resolveRosterModel()::where([ 'assignable_id' => $user->getKey(), 'assignable_type' => $user->getMorphClass(), 'roleable_id' => $target->getKey(), @@ -464,7 +467,7 @@ private function executeRoleCheck(AssignableEntity $user, RoleableEntity $target $plainKey = $role::getPlainKey(); // Check if there's a record with the plain text key - $exists = Roster::where([ + $exists = $this->resolveRosterModel()::where([ 'assignable_id' => $user->getKey(), 'assignable_type' => $user->getMorphClass(), 'roleable_id' => $target->getKey(), @@ -541,7 +544,7 @@ private function validateTenantIntegrity(AssignableEntity $user, RoleableEntity // Allow if user already has any role in the target's tenant (existing tenant participant) if ($roleableTenant !== null) { $tenantColumn = config('porter.multitenancy.tenant_column', 'tenant_id'); - $hasRoleInTenant = Roster::where([ + $hasRoleInTenant = $this->resolveRosterModel()::where([ 'assignable_type' => $user->getMorphClass(), 'assignable_id' => $user->getKey(), ])->where($tenantColumn, $roleableTenant)->exists(); @@ -589,6 +592,15 @@ private function resolveTenantIdForAssignment(AssignableEntity $user, RoleableEn return $user instanceof PorterAssignableContract ? $user->getPorterCurrentTenantKey() : null; } + /** + * @return class-string + */ + + private function resolveRosterModel(): string + { + return config('porter.models.roster', Roster::class); + } + /** * Destroy all role assignments for a specific tenant. * Cache will self-heal as stale entries expire and new queries return correct results. @@ -605,6 +617,6 @@ public function destroyTenantRoles(string $tenantKey): bool $tenantColumn = config('porter.multitenancy.tenant_column', 'tenant_id'); - return Roster::where($tenantColumn, $tenantKey)->delete() > 0; + return $this->resolveRosterModel()::where($tenantColumn, $tenantKey)->delete() > 0; } }