ApiService.php
11 months ago
ElementService.php
11 months ago
EmailService.php
11 months ago
FormService.php
11 months ago
LevelOrderService.php
11 months ago
LevelService.php
11 months ago
MembershipService.php
11 months ago
RedirectService.php
11 months ago
SanitizationService.php
11 months ago
StatisticsService.php
11 months ago
UserService.php
11 months ago
MembershipService.php
468 lines
| 1 | <?php declare(strict_types=1); |
| 2 | |
| 3 | namespace FapiMember\Service; |
| 4 | |
| 5 | use DateInterval; |
| 6 | use DateTimeImmutable; |
| 7 | use FapiMember\Container\Container; |
| 8 | use FapiMember\Model\Enums\Format; |
| 9 | use FapiMember\Model\Enums\Keys\MetaKey; |
| 10 | use FapiMember\Model\Enums\Types\LevelUnlockType; |
| 11 | use FapiMember\Model\Membership; |
| 12 | use FapiMember\Repository\LevelRepository; |
| 13 | use FapiMember\Repository\MembershipRepository; |
| 14 | use FapiMember\Repository\UserRepository; |
| 15 | use FapiMember\Utils\DateTimeHelper; |
| 16 | |
| 17 | class MembershipService |
| 18 | { |
| 19 | private MembershipRepository $membershipRepository; |
| 20 | private LevelRepository $levelRepository; |
| 21 | private UserRepository $userRepository; |
| 22 | |
| 23 | public function __construct() |
| 24 | { |
| 25 | $this->membershipRepository = Container::get(MembershipRepository::class); |
| 26 | $this->levelRepository = Container::get(LevelRepository::class); |
| 27 | $this->userRepository = Container::get(UserRepository::class); |
| 28 | } |
| 29 | |
| 30 | /** |
| 31 | * @return array<Membership> |
| 32 | */ |
| 33 | public function getActiveWithAccessByUserId(int $userId): array |
| 34 | { |
| 35 | return $this->membershipRepository->getActiveByUserId($userId, true); |
| 36 | } |
| 37 | |
| 38 | /** |
| 39 | * @return array<Membership> |
| 40 | */ |
| 41 | public function getActiveByUserIdAndUpdate(int $userId): array |
| 42 | { |
| 43 | $memberships = $this->membershipRepository->getActiveByUserId($userId); |
| 44 | $this->membershipRepository->saveAll($userId, $memberships); |
| 45 | |
| 46 | return $memberships; |
| 47 | } |
| 48 | |
| 49 | /** |
| 50 | * @param array<Membership> $memberships |
| 51 | */ |
| 52 | public function saveAll(int $userId, array $memberships): void |
| 53 | { |
| 54 | $this->membershipRepository->saveAll($userId, $memberships); |
| 55 | $this->fixMemberships($userId); |
| 56 | } |
| 57 | |
| 58 | public function fixMemberships(int $userId): void |
| 59 | { |
| 60 | $this->extendMembershipsToSections($userId); |
| 61 | $this->timeUnlockLevelsForUser($userId); |
| 62 | $this->extendSectionsDates($userId); |
| 63 | } |
| 64 | |
| 65 | public function saveOne(Membership $newMembership): void |
| 66 | { |
| 67 | if ( |
| 68 | $this->membershipRepository->getOneByUserIdAndLevelId( |
| 69 | $newMembership->getUserId(), |
| 70 | $newMembership->getLevelId(), |
| 71 | ) !== null |
| 72 | ) { |
| 73 | return; |
| 74 | } |
| 75 | |
| 76 | $memberships = $this->getActiveByUserIdAndUpdate($newMembership->getUserId()); |
| 77 | $memberships[] = $newMembership; |
| 78 | |
| 79 | $this->membershipRepository->saveAll($newMembership->getUserId(), $memberships); |
| 80 | } |
| 81 | |
| 82 | public function update(Membership $updatedMembership): void |
| 83 | { |
| 84 | $memberships = $this->getActiveByUserIdAndUpdate($updatedMembership->getUserId()); |
| 85 | $found = false; |
| 86 | |
| 87 | foreach ($memberships as $key => $membership) { |
| 88 | if ($membership->getLevelId() === $updatedMembership->getLevelId()) { |
| 89 | $memberships[$key] = $updatedMembership; |
| 90 | $found = true; |
| 91 | break; |
| 92 | } |
| 93 | } |
| 94 | |
| 95 | if ($found) { |
| 96 | $this->saveAll($updatedMembership->getUserId(), $memberships); |
| 97 | } |
| 98 | } |
| 99 | |
| 100 | /** @return array<mixed> */ |
| 101 | public function createOrUpdateMembership( |
| 102 | int $userId, |
| 103 | int $levelId, |
| 104 | DateTimeImmutable|null $registered, |
| 105 | DateTimeImmutable|null $until = null, |
| 106 | ): array |
| 107 | { |
| 108 | $oldMembership = $this->membershipRepository->getOneByUserIdAndLevelId($userId, $levelId); |
| 109 | |
| 110 | $newMembership = new Membership([ |
| 111 | 'level_id' => $levelId, |
| 112 | 'user_id' => $userId, |
| 113 | 'registered' => $registered, |
| 114 | 'until' => $until, |
| 115 | 'is_unlimited' => $until === null, |
| 116 | ]); |
| 117 | |
| 118 | if ($oldMembership !== null) { |
| 119 | $this->update($newMembership); |
| 120 | } else { |
| 121 | $this->saveOne($newMembership); |
| 122 | } |
| 123 | |
| 124 | $this->fixMemberships($userId); |
| 125 | |
| 126 | return [ |
| 127 | 'oldMembership' => $oldMembership, |
| 128 | 'newMembership' => $newMembership, |
| 129 | ]; |
| 130 | } |
| 131 | |
| 132 | /** @return array<mixed> */ |
| 133 | public function createOrProlongMembershipByDays( |
| 134 | int $userId, |
| 135 | int $levelId, |
| 136 | int|null $days = null, |
| 137 | ): array |
| 138 | { |
| 139 | $membership = $this->membershipRepository->getOneByUserIdAndLevelId($userId, $levelId); |
| 140 | |
| 141 | $isUnlimited = $days === null; |
| 142 | |
| 143 | if ($membership !== null) { |
| 144 | $props = $this->prolongMembership($membership, $isUnlimited, $days); |
| 145 | } else { |
| 146 | $props = $this->createMembershipFromDays($userId, $levelId, $isUnlimited, $days); |
| 147 | } |
| 148 | |
| 149 | $this->fixMemberships($userId); |
| 150 | |
| 151 | return $props; |
| 152 | } |
| 153 | |
| 154 | /** |
| 155 | * @return array<mixed> |
| 156 | * @throws \Exception |
| 157 | */ |
| 158 | public function createMembershipFromDays( |
| 159 | int $userId, |
| 160 | int $levelId, |
| 161 | $isUnlimited, |
| 162 | int|null $days = null, |
| 163 | ): array |
| 164 | { |
| 165 | $props = []; |
| 166 | $props['oldMembership'] = null; |
| 167 | |
| 168 | $registered = DateTimeHelper::getNow(); |
| 169 | |
| 170 | if ($isUnlimited) { |
| 171 | $until = null; |
| 172 | } else { |
| 173 | $until = $registered; |
| 174 | $until = $until->modify(sprintf('+ %s days', $days )); |
| 175 | } |
| 176 | |
| 177 | $newMembership = new Membership([ |
| 178 | 'level_id' => $levelId, |
| 179 | 'user_id' => $userId, |
| 180 | 'registered' => $registered, |
| 181 | 'until' => $until, |
| 182 | 'is_unlimited' => $isUnlimited |
| 183 | ]); |
| 184 | |
| 185 | $props['newMembership'] = $newMembership; |
| 186 | |
| 187 | $this->saveOne($newMembership); |
| 188 | |
| 189 | return $props; |
| 190 | } |
| 191 | |
| 192 | /** @return array<mixed> */ |
| 193 | public function prolongMembership( |
| 194 | Membership $membership, |
| 195 | bool $isUnlimited, |
| 196 | int|null $days = null, |
| 197 | ): array |
| 198 | { |
| 199 | $props = []; |
| 200 | $props['oldMembership'] = $membership; |
| 201 | |
| 202 | if ($isUnlimited || $membership->isUnlimited()) { |
| 203 | $membership->setIsUnlimited(true); |
| 204 | } else { |
| 205 | $membership->setUntil( |
| 206 | $membership->getUntil()->modify(sprintf('+ %s days', $days)) |
| 207 | ); |
| 208 | } |
| 209 | |
| 210 | $props['newMembership'] = $membership; |
| 211 | |
| 212 | $this->update($membership); |
| 213 | |
| 214 | return $props; |
| 215 | } |
| 216 | |
| 217 | public function extendMembershipsToSections(int $userId): void |
| 218 | { |
| 219 | $activeMemberships = $this->getActiveByUserIdAndUpdate($userId); |
| 220 | |
| 221 | if ($activeMemberships === []) { |
| 222 | return; |
| 223 | } |
| 224 | |
| 225 | /** @var array<Membership> $extendedMembership */ |
| 226 | $extendedMemberships = $activeMemberships; |
| 227 | $sectionsToExtend = []; |
| 228 | |
| 229 | foreach ($activeMemberships as $activeMembership) { |
| 230 | $level = $this->levelRepository->getLevelById($activeMembership->getLevelId()); |
| 231 | |
| 232 | if ($level !== null && !$level->isSection()) { |
| 233 | $sectionsToExtend[$level->getParentId()] = $this->levelRepository->getSectionById($level->getParentId()); |
| 234 | } |
| 235 | } |
| 236 | |
| 237 | foreach ($sectionsToExtend as $section) { |
| 238 | $childLevelIds = []; |
| 239 | |
| 240 | foreach ($section->getLevels() as $level) { |
| 241 | $childLevelIds[] = $level->getId(); |
| 242 | } |
| 243 | |
| 244 | $LevelMemberships = array_filter( |
| 245 | $activeMemberships, |
| 246 | static function ( $membership ) use ( $childLevelIds ) { |
| 247 | return in_array( $membership->getLevelId(), $childLevelIds, true ); |
| 248 | } |
| 249 | ); |
| 250 | |
| 251 | $childIsUnlimited = false; |
| 252 | $childMaxUntil = null; |
| 253 | $childMinRegistered = null; |
| 254 | |
| 255 | foreach ($LevelMemberships as $levelMembership ) { |
| 256 | if ( $levelMembership->isUnlimited() === true ) { |
| 257 | $childIsUnlimited = true; |
| 258 | } |
| 259 | |
| 260 | if ($levelMembership->getUntil() !== null) { |
| 261 | $childMaxUntil = max($childMaxUntil, $levelMembership->getUntil()); |
| 262 | } |
| 263 | |
| 264 | if ($levelMembership->getRegistered() !== null) { |
| 265 | if ($childMinRegistered === null) { |
| 266 | $childMinRegistered = $levelMembership->getRegistered(); |
| 267 | } else { |
| 268 | $childMinRegistered = min( |
| 269 | $childMinRegistered, |
| 270 | $levelMembership->getRegistered(), |
| 271 | ); |
| 272 | } |
| 273 | } |
| 274 | } |
| 275 | |
| 276 | $wasParentTermExtended = null; |
| 277 | |
| 278 | foreach ($extendedMemberships as $extendedMembership) { |
| 279 | if ($extendedMembership->getLevelId() === $section->getId()) { |
| 280 | if ($extendedMembership->isUnlimited()) { |
| 281 | $wasParentTermExtended = true; |
| 282 | break; |
| 283 | } |
| 284 | |
| 285 | if ($childIsUnlimited) { |
| 286 | $extendedMembership->setIsUnlimited(true); |
| 287 | $extendedMembership->setUntil(null); |
| 288 | $wasParentTermExtended = true; |
| 289 | break; |
| 290 | } |
| 291 | |
| 292 | $extendedMembership->setUntil( |
| 293 | max($extendedMembership->getUntil(), $childMaxUntil), |
| 294 | ); |
| 295 | $wasParentTermExtended = true; |
| 296 | } |
| 297 | } |
| 298 | |
| 299 | if (!$wasParentTermExtended) { |
| 300 | $newMembership = new Membership([ |
| 301 | 'level_id' => $section->getId(), |
| 302 | 'user_id' => $userId, |
| 303 | 'registered' => $childMinRegistered, |
| 304 | 'until' => $childMaxUntil, |
| 305 | 'is_unlimited' => $childIsUnlimited, |
| 306 | ]); |
| 307 | |
| 308 | $this->saveOne($newMembership); |
| 309 | } |
| 310 | } |
| 311 | } |
| 312 | |
| 313 | private function extendSectionsDates(int $userId): void |
| 314 | { |
| 315 | $sections = $this->levelRepository->getAllSections(); |
| 316 | |
| 317 | foreach ($sections as $section) { |
| 318 | $parentMembership = $this->membershipRepository->getOneByUserIdAndLevelId($userId, $section->getId()); |
| 319 | |
| 320 | $memberships = $this->membershipRepository->getAllLevelMembershipsByUserIdAndSectionId($userId, $section->getId()); |
| 321 | $newRegistered = null; |
| 322 | $newUntil = null; |
| 323 | $newIsUnlimited = null; |
| 324 | |
| 325 | |
| 326 | foreach ($memberships as $membership) { |
| 327 | if ($membership?->getRegistered() === null) { |
| 328 | continue; |
| 329 | } |
| 330 | |
| 331 | if ( |
| 332 | $membership->getRegistered() < $parentMembership->getRegistered() |
| 333 | && ($newRegistered === null || $membership->getRegistered() < $newRegistered) |
| 334 | ) { |
| 335 | $newRegistered = $membership->getRegistered(); |
| 336 | } |
| 337 | |
| 338 | if ( |
| 339 | $parentMembership->isUnlimited() !== true |
| 340 | && $membership->getUntil() !== null |
| 341 | && $membership->getUntil() > $parentMembership->getUntil() |
| 342 | && ($newUntil === null || $membership->getUntil() > $newUntil) |
| 343 | ) { |
| 344 | $newUntil = $membership->getUntil(); |
| 345 | } |
| 346 | |
| 347 | if ($membership->isUnlimited() && !$parentMembership->isUnlimited()) { |
| 348 | $newIsUnlimited = true; |
| 349 | } |
| 350 | } |
| 351 | |
| 352 | if ($newRegistered !== null) { |
| 353 | $parentMembership->setRegistered($newRegistered); |
| 354 | } |
| 355 | |
| 356 | if ($newIsUnlimited === true) { |
| 357 | $parentMembership->setIsUnlimited(true); |
| 358 | $parentMembership->setUntil(null); |
| 359 | } else if ($newUntil !== null) { |
| 360 | $parentMembership->setUntil($newUntil); |
| 361 | } |
| 362 | |
| 363 | if ($newUntil !== null || $newRegistered !== null || $newIsUnlimited === true) { |
| 364 | $this->update($parentMembership); |
| 365 | } |
| 366 | } |
| 367 | } |
| 368 | |
| 369 | public function timeUnlockLevelsForAllUsers(): void |
| 370 | { |
| 371 | $users = $this->userRepository->getAllUsers(); |
| 372 | |
| 373 | foreach ($users as $user) { |
| 374 | $this->timeUnlockLevelsForUser($user->getId()); |
| 375 | } |
| 376 | } |
| 377 | |
| 378 | public function timeUnlockLevelsForUser(int $userId): void |
| 379 | { |
| 380 | $memberships = $this->getActiveByUserIdAndUpdate($userId); |
| 381 | |
| 382 | foreach ($memberships as $membership){ |
| 383 | $section = $this->levelRepository->getSectionById($membership->getLevelId()); |
| 384 | |
| 385 | if ($section === null) { |
| 386 | continue; |
| 387 | } |
| 388 | |
| 389 | foreach ($section->getLevels() as $level) { |
| 390 | $unlockDate = null; |
| 391 | $unlockHour = $this->levelRepository->getHourUnlock($level->getId()); |
| 392 | |
| 393 | if ($level->getUnlockType() === LevelUnlockType::DAYS) { |
| 394 | $daysToUnlock = (int) get_term_meta($level->getId(), MetaKey::DAYS_TO_UNLOCK, true); |
| 395 | $date = $membership->getRegistered()->getTimestamp(); |
| 396 | $unlockDate = $date - ($date % 86400) + (86400 * $daysToUnlock) + (3600 * $unlockHour); |
| 397 | } elseif ($level->getUnlockType() === LevelUnlockType::DATE) { |
| 398 | $date = strtotime(get_term_meta($level->getId(), MetaKey::DATE_UNLOCK, true)); |
| 399 | $date = $date - ($date % 86400) + (3600 * $unlockHour); |
| 400 | |
| 401 | if ( |
| 402 | $this->levelRepository->getAfterDateUnlock($level->getId()) === false |
| 403 | || $membership->getRegistered()->getTimestamp() < $date |
| 404 | ) { |
| 405 | $unlockDate = $date; |
| 406 | } |
| 407 | } |
| 408 | |
| 409 | if ($unlockDate === null) { |
| 410 | continue; |
| 411 | } |
| 412 | |
| 413 | $unlockDate -= 60; |
| 414 | |
| 415 | if (DateTimeHelper::getNowTimestamp() >= $unlockDate) { |
| 416 | $this->saveOne(new Membership([ |
| 417 | 'level_id' => $level->getId(), |
| 418 | 'user_id' => $userId, |
| 419 | 'registered' => DateTimeHelper::getNow()->format(Format::DATE_TIME), |
| 420 | 'until' => $membership->getUntil()?->format(Format::DATE_TIME), |
| 421 | 'is_unlimited' => $membership->isUnlimited(), |
| 422 | ])); |
| 423 | } |
| 424 | } |
| 425 | } |
| 426 | } |
| 427 | |
| 428 | /** |
| 429 | * @throws \Exception |
| 430 | */ |
| 431 | public function getUnlockDate(int $levelId, int $userId, DateTimeImmutable|null $registrationDate = null): DateTimeImmutable|null |
| 432 | { |
| 433 | $level = $this->levelRepository->getLevelById($levelId); |
| 434 | |
| 435 | if ($level?->getParentId() === null || $level->getUnlockType() === null || $level->getUnlockType() === LevelUnlockType::NONE) { |
| 436 | return null; |
| 437 | } |
| 438 | |
| 439 | $date = null; |
| 440 | |
| 441 | if ($level->getUnlockType() === LevelUnlockType::DATE) { |
| 442 | $date = DateTimeHelper::createOrNull($this->levelRepository->getDateUnlock($level->getId()), Format::DATE); |
| 443 | |
| 444 | } elseif ($level->getUnlockType() === LevelUnlockType::DAYS) { |
| 445 | if ($registrationDate === null) { |
| 446 | $parentMembership = $this->membershipRepository->getOneByUserIdAndLevelId($userId, $level->getParentId()); |
| 447 | |
| 448 | if ($parentMembership === null) { |
| 449 | return null; |
| 450 | } |
| 451 | $registrationDate = $parentMembership->getRegistered(); |
| 452 | } |
| 453 | |
| 454 | $date = $registrationDate->add( |
| 455 | new DateInterval('P' . $this->levelRepository->getDaysUnlock($level->getId()) . 'D'), |
| 456 | ); |
| 457 | } |
| 458 | |
| 459 | if ($date !== null) { |
| 460 | $hour = $this->levelRepository->getHourUnlock($level->getId()); |
| 461 | $date = $date->setTime($hour, 0); |
| 462 | } |
| 463 | |
| 464 | return $date; |
| 465 | } |
| 466 | |
| 467 | } |
| 468 |