> */ public function listForUser(int $userId, int $limit = 50): array { if ($userId <= 0) { return []; } return $this->notificationRepository->listByUser($userId, $limit, 0); } public function unreadCount(int $userId): int { if ($userId <= 0) { return 0; } return $this->notificationRepository->countUnreadByUser($userId); } public function markAsRead(int $userId, int $notificationId): bool { if ($userId <= 0 || $notificationId <= 0) { return false; } return $this->notificationRepository->markRead($notificationId, $userId); } public function markAllAsRead(int $userId): int { if ($userId <= 0) { return 0; } return $this->notificationRepository->markAllReadByUser($userId); } public function dismiss(int $userId, int $notificationId): bool { if ($userId <= 0 || $notificationId <= 0) { return false; } return $this->notificationRepository->delete($notificationId, $userId); } public function createForUser( int $recipientUserId, ?int $tenantId, string $type, string $title, ?string $body, ?string $link, array $data ): int|false { if ($recipientUserId <= 0 || $type === '' || $title === '') { return false; } return $this->notificationRepository->create([ 'recipient_user_id' => $recipientUserId, 'tenant_id' => $tenantId, 'type' => $type, 'title' => $title, 'body' => $body, 'link' => $link, 'data' => $data ?: null, ]); } /** * Fan-out: create notification for all active users in a tenant, optionally excluding actor. * * @return int Number of notifications created */ public function createForTenantUsers( int $tenantId, string $type, string $title, ?string $body, ?string $link, array $data, ?int $excludeUserId ): int { if ($tenantId <= 0 || $type === '' || $title === '') { return 0; } $userIds = $this->listUserIdsForTenant($tenantId); $created = 0; foreach ($userIds as $userId) { if ($excludeUserId !== null && $userId === $excludeUserId) { continue; } $result = $this->createForUser($userId, $tenantId, $type, $title, $body, $link, $data); if ($result !== false) { $created++; } } return $created; } /** * Fan-out: create notification only for tenant users whose ID is in $allowedUserIds. * * @param array $allowedUserIds Flipped array (user ID as key) for fast lookup * @return int Number of notifications created */ public function createForTenantAdminUsers( int $tenantId, string $type, string $title, ?string $body, ?string $link, array $data, ?int $excludeUserId, array $allowedUserIds ): int { if ($tenantId <= 0 || $type === '' || $title === '' || $allowedUserIds === []) { return 0; } $userIds = $this->listUserIdsForTenant($tenantId); $created = 0; foreach ($userIds as $userId) { if (!isset($allowedUserIds[$userId])) { continue; } if ($excludeUserId !== null && $userId === $excludeUserId) { continue; } $result = $this->createForUser($userId, $tenantId, $type, $title, $body, $link, $data); if ($result !== false) { $created++; } } return $created; } public function purgeOldRead(int $retentionDays = 90): int { return $this->notificationRepository->purgeReadOlderThanDays($retentionDays); } public function deleteAllForUser(int $userId): int { if ($userId <= 0) { return 0; } return $this->notificationRepository->deleteAllByUser($userId); } /** * Look up active user IDs belonging to a tenant. * * @return list */ private function listUserIdsForTenant(int $tenantId): array { if ($tenantId <= 0) { return []; } $rows = \MintyPHP\DB::select( 'select ut.user_id from user_tenants ut join users u on u.id = ut.user_id and u.active = 1 where ut.tenant_id = ?', (string) $tenantId ); if (!is_array($rows)) { return []; } $ids = []; foreach ($rows as $row) { $data = $row['user_tenants'] ?? $row; if (is_array($data) && isset($data['user_id'])) { $ids[] = (int) $data['user_id']; } } return array_values(array_unique($ids)); } }