hasSession()) ? $request->session() : session(); $id = $session->get(self::SESSION_KEY); return is_int($id) ? $id : (is_numeric($id) ? (int) $id : null); } public function currentWorkspace(?Request $request = null): ?Workspace { $id = $this->currentWorkspaceId($request); if (! $id) { return null; } $workspace = Workspace::query()->whereKey($id)->first(); if (! $workspace) { return null; } if (! $this->isWorkspaceSelectable($workspace)) { return null; } return $workspace; } public function setCurrentWorkspace(Workspace $workspace, ?User $user = null, ?Request $request = null): void { $session = ($request && $request->hasSession()) ? $request->session() : session(); $session->put(self::SESSION_KEY, (int) $workspace->getKey()); if ($user !== null) { $user->forceFill(['last_workspace_id' => (int) $workspace->getKey()])->save(); } } public function rememberLastTenantId(int $workspaceId, int $tenantId, ?Request $request = null): void { $session = ($request && $request->hasSession()) ? $request->session() : session(); $map = $session->get(self::LAST_TENANT_IDS_SESSION_KEY, []); $map = is_array($map) ? $map : []; $map[(string) $workspaceId] = $tenantId; $session->put(self::LAST_TENANT_IDS_SESSION_KEY, $map); } public function rememberTenantContext(Tenant $tenant, ?Request $request = null): bool { $workspaceId = $this->currentWorkspaceId($request); if ($workspaceId === null || (int) $tenant->workspace_id !== $workspaceId) { return false; } $outcome = $this->tenantOperabilityService->outcomeFor( tenant: $tenant, actor: $request?->user() instanceof User ? $request->user() : auth()->user(), workspaceId: $workspaceId, lane: TenantInteractionLane::StandardActiveOperating, question: TenantOperabilityQuestion::RememberedContextValidity, ); if (! $outcome->allowed) { $this->clearLastTenantId($request); return false; } if (! $this->userCanAccessTenant($tenant, $request)) { $this->clearRememberedTenantContext($request); return false; } $this->rememberLastTenantId($workspaceId, (int) $tenant->getKey(), $request); return true; } public function lastTenantId(?Request $request = null): ?int { $workspaceId = $this->currentWorkspaceId($request); if ($workspaceId === null) { return null; } $session = ($request && $request->hasSession()) ? $request->session() : session(); $map = $session->get(self::LAST_TENANT_IDS_SESSION_KEY, []); $map = is_array($map) ? $map : []; $id = $map[(string) $workspaceId] ?? null; return is_int($id) ? $id : (is_numeric($id) ? (int) $id : null); } public function clearLastTenantId(?Request $request = null): void { $workspaceId = $this->currentWorkspaceId($request); if ($workspaceId === null) { return; } $session = ($request && $request->hasSession()) ? $request->session() : session(); $map = $session->get(self::LAST_TENANT_IDS_SESSION_KEY, []); $map = is_array($map) ? $map : []; unset($map[(string) $workspaceId]); $session->put(self::LAST_TENANT_IDS_SESSION_KEY, $map); } public function clearRememberedTenantContext(?Request $request = null): void { $this->clearLastTenantId($request); } public function rememberedTenant(?Request $request = null): ?Tenant { $workspaceId = $this->currentWorkspaceId($request); if ($workspaceId === null) { return null; } $rememberedTenantId = $this->lastTenantId($request); if ($rememberedTenantId === null) { return null; } $tenant = Tenant::query() ->withTrashed() ->whereKey($rememberedTenantId) ->first(); if (! $tenant instanceof Tenant) { $this->clearRememberedTenantContext($request); return null; } if ((int) $tenant->workspace_id !== $workspaceId) { $this->clearRememberedTenantContext($request); return null; } if (! $this->userCanAccessTenant($tenant, $request)) { $this->clearRememberedTenantContext($request); return null; } $outcome = $this->tenantOperabilityService->outcomeFor( tenant: $tenant, actor: $request?->user() instanceof User ? $request->user() : auth()->user(), workspaceId: $workspaceId, lane: TenantInteractionLane::StandardActiveOperating, question: TenantOperabilityQuestion::RememberedContextValidity, ); if (! $outcome->allowed) { $this->clearRememberedTenantContext($request); return null; } return $tenant; } public function clearCurrentWorkspace(?User $user = null, ?Request $request = null): void { $session = ($request && $request->hasSession()) ? $request->session() : session(); $session->forget(self::SESSION_KEY); if ($user !== null && $user->last_workspace_id !== null) { $user->forceFill(['last_workspace_id' => null])->save(); } } public function resolveInitialWorkspaceFor(User $user, ?Request $request = null): ?Workspace { $session = ($request && $request->hasSession()) ? $request->session() : session(); $currentId = $this->currentWorkspaceId($request); if ($currentId) { $current = Workspace::query()->whereKey($currentId)->first(); if (! $current instanceof Workspace || ! $this->isWorkspaceSelectable($current) || ! $this->isMember($user, $current)) { $session->forget(self::SESSION_KEY); if ((int) $user->last_workspace_id === (int) $currentId) { $user->forceFill(['last_workspace_id' => null])->save(); } } else { return $current; } } if ($user->last_workspace_id !== null) { $workspace = Workspace::query()->whereKey($user->last_workspace_id)->first(); if (! $workspace instanceof Workspace || ! $this->isWorkspaceSelectable($workspace) || ! $this->isMember($user, $workspace)) { $user->forceFill(['last_workspace_id' => null])->save(); } } $memberships = WorkspaceMembership::query() ->where('user_id', $user->getKey()) ->with('workspace') ->get(); $selectableWorkspaces = $memberships ->map(fn (WorkspaceMembership $membership) => $membership->workspace) ->filter(fn (?Workspace $workspace) => $workspace instanceof Workspace && $this->isWorkspaceSelectable($workspace)) ->values(); if ($selectableWorkspaces->count() === 1) { /** @var Workspace $workspace */ $workspace = $selectableWorkspaces->first(); $session->put(self::SESSION_KEY, (int) $workspace->getKey()); $user->forceFill(['last_workspace_id' => (int) $workspace->getKey()])->save(); return $workspace; } return null; } public function isMember(User $user, Workspace $workspace): bool { return WorkspaceMembership::query() ->where('user_id', $user->getKey()) ->where('workspace_id', $workspace->getKey()) ->exists(); } public function currentWorkspaceForMemberOrFail(User $user, ?Request $request = null): Workspace { $workspace = $this->currentWorkspace($request); if (! $workspace instanceof Workspace || ! $this->isMember($user, $workspace)) { throw new NotFoundHttpException; } return $workspace; } public function ensureTenantAccessibleInCurrentWorkspace(Tenant $tenant, User $user, ?Request $request = null): Tenant { $workspace = $this->currentWorkspaceForMemberOrFail($user, $request); if ((int) $tenant->workspace_id !== (int) $workspace->getKey() || ! $user->canAccessTenant($tenant)) { throw new NotFoundHttpException; } return $tenant; } private function isWorkspaceSelectable(Workspace $workspace): bool { return empty($workspace->archived_at); } private function userCanAccessTenant(Tenant $tenant, ?Request $request = null): bool { $user = $request?->user(); if (! $user instanceof User) { $user = auth()->user(); } return $user instanceof User && $user->canAccessTenant($tenant); } }