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